1 /*****************************************************************************
2    Major portions of this software are copyrighted by the Medical College
3    of Wisconsin, 1994-2000, and are released under the Gnu General Public
4    License, Version 2.  See the file README.Copyright for details.
5 ******************************************************************************/
6 
7 /*----------------------------------------------------------------------------*/
8 /* Please note that this file is not "integrated" into AFNI. By this I
9    mean that it doesn't know about AFNI controllers, datasets, and so on.
10    It is an image viewing server, and the caller needs to provide a
11    callback function to get data to be displayed, and also provide some
12    other info.  The callback function provide a sequence of 2D images
13    to be displayed at the user's whim.  Besides being used in the AFNI GUI
14    to view slices from a 3D dataset, the imseq functionality is also used
15    in the Rendering plugin to view 3D renderings, in the aiv program for
16    generic image viewing, and in SUMA for capturing image snapshots.
17 *//*--------------------------------------------------------------------------*/
18 
19 /*----------------------------------------------------------------------------*/
20 /* Structure of this AFNI image viewer:
21 
22     * An image viewer's data structure is an MCW_imseq, created by
23       calling open_MCW_imseq(). Each AFNI controller has 3 MCW_imseq's:
24       the axial, sagittal, and coronal viewers.
25 
26     * The prototype is
27         MCW_imseq * open_MCW_imseq( MCW_DC *dc ,
28                                     get_ptr get_image , XtPointer aux )
29          dc        = "display context" (display.[ch]) that holds the
30                      information about X11 display, colors, fonts,
31                      graphics contexts, .... This information is
32                      used when actually drawing to the display.
33          get_image = function that returns a pointer, which is the
34                      way the MCW_imseq gets information from AFNI.
35          aux       = pointer to any information that AFNI wants when
36                      get_image() is called -- information that lets
37                      AFNI identify what image viewer is calling.
38 
39     * get_image() is called by get_image(n,type,aux), where
40          n         = (int) calling parameter - for example, image index
41          type      = (int) one of the isqCR_xxx codes in imseq.h,
42                      indicating the "Callback Reason" -- what AFNI is
43                      to do with this call.
44          aux       = the same "aux" as above, to give back to AFNI the
45                      auxiliary information needed to identify this viewer.
46       The type of object pointed to by get_image()'s return depends on
47       "type" -- at this date [Jan 2021] there are 33 isqCR_xxx codes.
48 
49     * In turn, AFNI can control what the image viewer does by calling
50       the function drive_MCW_imseq(), which will carry out some action
51       specified by one of the isqDR_xxx "Drive Reason" codes in imseq.h.
52       At this date, there are 73 such isqDR_xxx codes.
53 
54     * And of course, the user can control what the viewer does by using
55       the Widgets created in open_MCW_imseq(). Most user actions also
56       result in a call to get_image() to inform AFNI about the change;
57       for example, if the slice viewed is changed, AFNI needs to know
58       to update the crosshairs, the displayed coordinates, etc.
59 
60     * This image viewer is also used in programs aiv, to3d, Xphace (a toy),
61       and in SUMA via the ISQ_snap*() functions -- which are located
62       in imseq.c and xim.c, and provide a way for a program to snapshot
63       an image or a Widget and save it to a viewer or directly to a file.
64 
65     * Over the years (starting July 1994), the image viewer has grown
66       to have more and more functionality, which makes the code below
67       more and more ugly. However, the basic structure is not complicated:
68          User does something
69            imseq.c may make a change itself (e.g., Zoom current image)
70            or it may call AFNI to do something (e.g., provide a new image)
71          AFNI drives viewer to do something
72            which may in turn call AFNI back to provide data
73            (there are safeguards to prevent infinite recursion)
74       The ugliness comes from the many operations included in the word
75       "something" above, and the necessity for providing controls and
76       functions for all of them. And from the fact that the image viewer
77       has far outgrown its original scope, and pieces were added over
78       Lo These Many Years, so the code is kind of a mess.
79 
80     * As a measure of the growth of the code, on 1997-11-05 (the date
81       I put AFNI under code version control via SCCS), there were 3409
82       lines in this file. As of right now (2021-01-13), the line count
83       has expanded to 14510 -- 426% growth over 23 years.
84 
85     * Good luck, and be careful in here -- Bob Cox.
86 *//*--------------------------------------------------------------------------*/
87 
88 #include <stdio.h>
89 #include <string.h>
90 #include <errno.h>       /* 01 May 2003 - rickr */
91 #include <X11/keysym.h>  /* 24 Jan 2003 */
92 
93 #undef IMSEQ_DEBUG
94 
95 #include "mrilib.h"
96 #include "imseq.h"
97 #include "xutil.h"
98 #include "xim.h"
99 
100 #if 0                   /* used to debug only this file */
101 # undef  DBG_trace
102 # define DBG_trace 2
103 #endif
104 
105 #define DPRI(st,ijk) \
106   if(PRINT_TRACING){ char str[256]; sprintf(str,"%s %d",st,ijk); STATUS(str); }
107 
108 #define COLSIZE AV_colsize()  /* for optmenus -- 11 Dec 2001 */
109 
110 #define DONT_ONOFF_ONE        /* 29 Jul 2002 */
111 
112 /* macro to send information to AFNI */
113 #define SEND(sq,cb)                                     \
114   AFNI_CALL_VOID_3ARG( (sq)->status->send_CB ,          \
115                        MCW_imseq * , sq ,               \
116                        XtPointer   , (sq)->getaux ,     \
117                        ISQ_cbs *   , &(cb)          )
118 
119 static Widget wwtem ;
120 
121 #define SWL_TMASK_DEFAULT (Mod1Mask | Mod2Mask)
122 
123 static int scrollwheel_tmask = SWL_TMASK_DEFAULT ;
124 static int scrollwheel_debug = 0 ;
125 
126 void ISQ_set_scale( Widget wscal , int percent ) ;
127 void ISQ_render_scal_CB( Widget w, XtPointer client_data, XtPointer call_data ) ;
128 void ISQ_popdown_render_scal( MCW_imseq *seq ) ;
129 void ISQ_popup_render_scal( MCW_imseq *seq ) ;
130 
131 /* stuff for the VG effect */
132 
133 static float vgize_sigfac = 0.02f ;
134 static MRI_IMAGE * mri_vgize( MRI_IMAGE *im ) ;
135 #define VGFAC(sss) \
136   ( ((sss)->opt.improc_code & ISQ_IMPROC_VG) ? (sss)->vgize_fac : 0.0f )
137 #if 1
138 #  define VGSCAL 1.27537f
139 #  define INDEX_TO_VGFAC(qq) ( powf(VGSCAL,(float)((qq)-1))*0.01f )
140 #  define VGFAC_TO_INDEX(vf) ( (int)(logf(100.01f*(vf))/logf(VGSCAL)+1.01f))
141 #else
142 #  define INDEX_TO_VGFAC(qq) (0.01f*(qq))
143 #  define VGFAC_TO_INDEX(vf) ((int)(100.01f*(vf)))
144 #endif
145 
146 /************************************************************************
147    Define the buttons and boxes that go in the "Disp" dialog
148 *************************************************************************/
149 
150 /*-- structures defining action buttons (at bottom of dialog) --*/
151 
152 #define NACT_DISP 2  /* number of action buttons */
153 #define DISP_OK   1  /* indices for button labels */
154 #define DISP_UNDO 0
155 
156 /*** do NOT re-enable group statistics -- it's very old code ***/
157 #define NO_GROUP_SCALE  /* button box NTOG_SCL will be disabled */
158 #ifdef  NO_GROUP_SCALE  /* and no group statistics will be computed */
159 # undef AUTOMATE_STATISTICS
160 #endif
161 
162 static MCW_action_item ISQ_disp_act[NACT_DISP] = {
163  {"Reset",ISQ_disp_act_CB,NULL,"Sets all options back\nto earlier values","Undo changes", 0 },
164  {"Done" ,ISQ_disp_act_CB,NULL,"Closes this window"                      ,"Close window", 1 }
165 } ;
166 
167 /*-- structures defining the 9 toggle button boxes --*/
168 
169                        /* number of buttons in each button box */
170 #define NBUT_DISP1  4  /* Rotation box */
171 #define NBUT_DISP2  1  /* Mirror box */
172 #define NBUT_DISP3  1  /* No overlay box */
173 #define NBUT_DISP4  3  /* Range scaling box */
174 #define NBUT_DISP5  2  /* Auto- or Group- scale box [will be hidden] */
175 #define NBUT_DISP6  1  /* Free aspect box */
176 #define NBUT_DISP7  2  /* Save box */          /* 26 Jul 2001: was 3, now 2 */
177 #define NBUT_DISP8  4  /* IMPROC buttons */
178 #define NBUT_DISP9  4  /* CX buttons */
179 
180 #define NTOG_ROT  0    /* index of which button box control which option(s) */
181 #define NTOG_MIR  1
182 #define NTOG_COL  2
183 #define NTOG_RNG  3
184 #define NTOG_SCL  4
185 #define NTOG_ASP  5
186 #define NTOG_SAV  6
187 #define NTOG_IMP  7
188 #define NTOG_CX   8
189 
190 #define ALLOW_CLIPPING(ss,tt)                                    \
191  do{ int za = (int)(tt) ;                                        \
192      int zb = ( ISQ_REALZ(ss) && (ss)->dialog != NULL &&         \
193                 (ss)->dialog_starter == NBUT_DISP     &&         \
194                 (ss)->bbox[NTOG_RNG] != NULL            ) ;      \
195      if(zb) SENSITIZE((ss)->bbox[NTOG_RNG]->wbut[2],za) ;        \
196      if( (ss)->opt.scale_range == ISQ_RNG_CLIPPED && !za ){      \
197        (ss)->opt.scale_range = ISQ_RNG_02TO98 ;                  \
198        if(zb) MCW_set_bbox((ss)->bbox[NTOG_RNG],ISQ_RNG_02TO98); \
199      } else if( za && (ss)->redo_clip ){                         \
200        (ss)->opt.scale_range = ISQ_RNG_CLIPPED ;                 \
201        if(zb) MCW_set_bbox((ss)->bbox[NTOG_RNG],ISQ_RNG_CLIPPED);\
202      }                                                           \
203  } while(0)
204 
205 /* labels for the toggle buttons in each box */
206 
207 static char * ISQ_dl1[NBUT_DISP1] = {
208    "No Rotation" , "CCW 90" , "Rot 180" , "CW 90" } ;
209 static char * ISQ_dl2[NBUT_DISP2] = { "+ LR Mirror" } ;
210 static char * ISQ_dl3[NBUT_DISP3] = { "No Overlay" } ;
211 static char * ISQ_dl4[NBUT_DISP4] = { "Min-to-Max" , "2%-to-98%" , "Clipped" } ;
212 static char * ISQ_dl5[NBUT_DISP5] = { "Autoscale" , "Groupscale" } ;
213 static char * ISQ_dl6[NBUT_DISP6] = { "Free Aspect" } ;
214 static char * ISQ_dl7[NBUT_DISP7] = { "Nsize Save" , "PNM Save" } ;
215 static char * ISQ_dl8[NBUT_DISP8] = { "Flatten" , "Sharpen" , "Edge Detect" , "VG paint" } ;
216 static char * ISQ_dl9[NBUT_DISP9] = {
217    "Complex->Mag" , "Complex->Arg" , "Complex->Real" , "Complex->Imag" } ;
218 
219 /* collect info about boxes, including the type (see bbox.c):
220      radio_one  = exactly one button can be pressed in
221      radio_zero = zero or one button can be pressed in
222      check      = any combination of buttons can be pressed in */
223 
224 static ISQ_boxdef ISQ_dispbb[] = {
225    { NBUT_DISP1 , ISQ_dl1 , MCW_BB_radio_one  , MCW_BB_frame } ,
226    { NBUT_DISP2 , ISQ_dl2 , MCW_BB_check      , MCW_BB_frame } ,
227    { NBUT_DISP3 , ISQ_dl3 , MCW_BB_check      , MCW_BB_frame } ,
228    { NBUT_DISP4 , ISQ_dl4 , MCW_BB_radio_one  , MCW_BB_frame } ,
229    { NBUT_DISP5 , ISQ_dl5 , MCW_BB_radio_one  , MCW_BB_frame } ,
230    { NBUT_DISP6 , ISQ_dl6 , MCW_BB_check      , MCW_BB_frame } ,
231    { NBUT_DISP7 , ISQ_dl7 , MCW_BB_radio_zero , MCW_BB_frame } ,
232    { NBUT_DISP8 , ISQ_dl8 , MCW_BB_check      , MCW_BB_frame } ,
233    { NBUT_DISP9 , ISQ_dl9 , MCW_BB_radio_one  , MCW_BB_frame } ,
234 } ;
235 
236 /* helps and hints for the ignorant */
237 
238 static char * ISQ_bb1_help[NBUT_DISP1] = {
239    "Sets orientation to the\noriginal in the data set" ,
240    "Rotate 90 degrees\ncounterclockwise\nfrom original" ,
241    "Rotate 180 degrees\nfrom original" ,
242    "Rotate 90 degrees\nclockwise\nfrom original"
243 } ;
244 
245 static char * ISQ_bb1_hint[NBUT_DISP1] = {
246    "No extra rotation of image" ,
247    "90 degrees counterclockwise" ,
248    "Rotate 180 degrees" ,
249    "90 degrees clockwise"
250 } ;
251 
252 static char * ISQ_bb2_help[NBUT_DISP2] = {
253    "pressed IN means\nleft-right mirror AFTER rotation"
254 } ;
255 
256 static char * ISQ_bb2_hint[NBUT_DISP2] = {
257    "IN: mirror image AFTER rotation"
258 } ;
259 
260 static char * ISQ_bb3_help[NBUT_DISP3] = {
261    "pressed IN means\nturn color overlays off"
262 } ;
263 
264 static char * ISQ_bb3_hint[NBUT_DISP3] = {
265    "IN: turn color overlays off"
266 } ;
267 
268 static char * ISQ_bb4_help[NBUT_DISP4] = {
269  "Intensities mapped\nover full range of data\n(min->lowest,max->highest)" ,
270  "Intensities mapped\nover partial range of data\n(%ages in data histogram)" ,
271  "Intensities mapped\nover auto-clipped range\nof data in all images"
272 } ;
273 
274 static char * ISQ_bb4_hint[NBUT_DISP4] = {
275  "Background intensity = min to max pixel values" ,
276  "Background intensity = 2% to 98% pixel values"  ,
277  "Background intensity = auto-clipped from volume"
278 } ;
279 
280 static char * ISQ_bb5_help[NBUT_DISP5] = {
281    "Intensities mapped\nfor each image separately" ,
282    "Intensities mapped\nfor all images in common"
283 } ;
284 
285 static char * ISQ_bb5_hint[NBUT_DISP5] = {
286    "Intensities computed for each slice" ,
287    "Intensities computed for all slices at once"
288 } ;
289 
290 static char * ISQ_bb6_help[NBUT_DISP6] = {
291  "pressed IN means allow arbitrary resizing of window\n"
292  "pressed OUT means restrict window aspect ratio"
293 } ;
294 
295 static char * ISQ_bb6_hint[NBUT_DISP6] = {
296  "IN: Allow arbitrary resizing of window"
297 } ;
298 
299 static char * ISQ_bb7_help[NBUT_DISP7] = {
300  "Nsize: IN = 'normal' (power of 2)  saved images sizes\n"
301  "       OUT= 'natural' (data given) saved images sizes"   ,
302 
303  "PNM:   IN = saved images are color (PNM format)\n"
304  "       OUT= saved images are background data only\n"
305 } ;
306 
307 static char * ISQ_bb7_hint[NBUT_DISP7] = {
308  "IN: Save background images in power-of-2 sizes" ,
309  "IN: Save background images in PNM format"
310 } ;
311 
312 static char * ISQ_bb8_help[NBUT_DISP8] = {
313  "Flatten: IN = Flatten histogram of background\n"
314  "         OUT= Don't flatten histogram\n"                      ,
315 
316  "Sharpen: IN = Apply sharpening filter to background\n"
317  "         OUT= Don't apply sharpening filter"                  ,
318 
319  "Edge: IN = Use Sobel edge detection filter on background\n"
320  "      OUT= Don't use Sobel edge detector"                     ,
321 
322  "VG: IN = Apply a painting effect to the image.\n"
323  "    OUT= Don't do this (which is just for fun)."
324 } ;
325 
326 static char * ISQ_bb8_hint[NBUT_DISP8] = {
327  "Flatten histogram of background" ,
328  "Apply sharpening filter to background" ,
329  "Apply Sobel edge detector to background" ,
330  "Apply painting effect (for fun)"
331 } ;
332 
333 #define ISQ_CX_HELP                            \
334   "Complex-> options control how complex-\n"   \
335   "valued images are displayed:\n"             \
336   "  ->Mag  == Display magnitude\n"            \
337   "  ->Arg  == Display argument (phase)\n"     \
338   "  ->Real == Display real part\n"            \
339   "  ->Imag == Display imaginary part"
340 
341 static char * ISQ_bb9_help[NBUT_DISP9] = {
342   ISQ_CX_HELP , ISQ_CX_HELP , ISQ_CX_HELP , ISQ_CX_HELP
343 } ;
344 
345 static char * ISQ_bb9_hint[NBUT_DISP9] = {
346   "Display magnitude" ,
347   "Display argument (phase)"
348   "Display real part" ,
349   "Display imaginary part"
350 } ;
351 
352 static char ** ISQ_bb_allhelp[] = {
353   ISQ_bb1_help , ISQ_bb2_help , ISQ_bb3_help ,
354   ISQ_bb4_help , ISQ_bb5_help , ISQ_bb6_help ,
355   ISQ_bb7_help , ISQ_bb8_help , ISQ_bb9_help
356 } ;
357 
358 static char ** ISQ_bb_allhint[] = {
359   ISQ_bb1_hint , ISQ_bb2_hint , ISQ_bb3_hint ,
360   ISQ_bb4_hint , ISQ_bb5_hint , ISQ_bb6_hint ,
361   ISQ_bb7_hint , ISQ_bb8_hint , ISQ_bb9_hint
362 } ;
363 /*************************************************************************/
364 
365 /*------ 27 Jun 2001: list of external programs that may be of use ------*/
366 
367 /* ppmto = programs to take as image input the simple PPM format
368            and convert the image to something more useful, like JPEG */
369 
370 static char ** ppmto_filter  = NULL ;
371 static char ** ppmto_suffix  = NULL ;
372 static int   * ppmto_bval    = NULL ;
373 static int     ppmto_num     = -1 ;
374 static int   * ppmto_gimpize = NULL ;
375 
376 static char *  ppmto_gif_filter  = NULL ;   /* 27 Jul 2001 */
377 static char *  ppmto_agif_filter = NULL ;
378 
379 #define USE_GIFF  /* use Fixed colormap GIF for animations */
380 #ifdef  USE_GIFF
381 static char *  ppmto_giff_filter = NULL ;   /* 05 Oct 2004 */
382 #define GIFF_MAPFILE "Qwerty53211.ppm"      /* 53211 was my Zip code in WI */
383 #endif
384 
385 static char *  ppmto_mpeg_filter = NULL ;   /* 02 Aug 2001 */
386 static char *  ppmto_ppm_filter  = NULL ;
387 
388 static char *  ppmto_jpg75_filter = NULL ;  /* 27 Mar 2002 */
389 static char *  ppmto_jpg95_filter = NULL ;  /* 28 Jul 2005 */
390 static char *  ppmto_png_filter   = NULL ;  /* 07 Dec 2006 */
391 
392 static char *  gimp_path          = NULL ;  /* 27 Oct 2017 */
393 
394  /* the first %s will be the list of input gif filenames     */
395  /* the second %s is the single output animated gif filename */
396 
397 #define GIFSICLE_SUFFIX    "-O2 -d %d -k 127 -l %%s > %%s"
398 #define WHIRLGIF_SUFFIX    "-time %d -loop %%s > %%s"
399 #define FFMPEG_SUFFIX      "-loglevel quiet -y"
400 
401 #define DO_AGIF(sq) ((sq)->opt.save_agif)
402 #define DO_MPEG(sq) ((sq)->opt.save_mpeg)
403 #define DO_ANIM(sq) (DO_AGIF(sq) || DO_MPEG(sq))
404 
405 /* below: the first DO_BLOWUP will let montages be blown up
406           the second will not [20 Dec 2016]                 */
407 
408 #if 1
409 # define DO_BLOWUP(sss)                                  \
410    ((sss)->zoom_fac > 1 || (sss)->saver_blowup > 1)
411 #else
412 # define DO_BLOWUP(sss)                                  \
413    ( ((sss)->zoom_fac > 1 || (sss)->saver_blowup > 1) && \
414      ((sss)->mont_nx == 1 && (sss)->mont_ny     == 1)      )
415 #endif
416 
417 #define ADDTO_PPMTO(pnam,suff,bbb,ggg)                                   \
418   do{ ppmto_filter = (char **) realloc( ppmto_filter ,                   \
419                                         sizeof(char *)*(ppmto_num+1) ) ; \
420       ppmto_suffix = (char **) realloc( ppmto_suffix  ,                  \
421                                         sizeof(char *)*(ppmto_num+1) ) ; \
422       ppmto_bval   = (int *)   realloc( ppmto_bval    ,                  \
423                                         sizeof(int)   *(ppmto_num+1) ) ; \
424       ppmto_gimpize= (int *)   realloc( ppmto_gimpize ,                  \
425                                         sizeof(int)   *(ppmto_num+1) ) ; \
426       ppmto_filter [ppmto_num] = (pnam) ;                                \
427       ppmto_suffix [ppmto_num] = (suff) ;                                \
428       ppmto_bval   [ppmto_num] = (bbb)  ;                                \
429       ppmto_gimpize[ppmto_num] = (ggg)&&(gimp_path!=NULL); ppmto_num++;  \
430       if( dbg ) fprintf(stderr,"IMSAVE: filter '%s' for suffix '%s'\n",  \
431                         (pnam) , (suff) ) ;                              \
432   } while(0)
433 
434   /** 16 Nov 2004: warning messages when can't find Save filters? **/
435 
436 #define CANT_FIND(nm,fm)                                                 \
437  do{ if( !AFNI_noenv("AFNI_IMSAVE_WARNINGS") ){                          \
438       if( ncant == 0 )                                                   \
439        fprintf(stderr,"\n++++++++ IMAGE SAVE SETUP WARNINGS ++++++++\n");\
440       fprintf(stderr,                                                    \
441               "++ Can't find program %s for Save to %s\n",(nm),(fm)) ;   \
442      } ncant++ ;                                                         \
443  } while(0)
444 
445 /*------------------------------------------------------------------*/
446 /*---- Setup programs as filters: ppm stdin to some output file ----*/
447 /*---- Doing image output this way avoids having to incorporate ----*/
448 /*---- external libraries, but means NETPBM needs to be present ----*/
449 /*------------------------------------------------------------------*/
450 
ISQ_setup_ppmto_filters(void)451 void ISQ_setup_ppmto_filters(void)
452 {
453    char *pg , *pg2 , *str , *eee ;
454    int bv ;
455    int dbg ;
456    int ncant=0 , need_netpbm=0 ;  /* 16 Nov 2004 */
457    int jpeg_compress;
458 
459    ppmto_num = 0 ; bv = ISQ_SAV_PNM ;
460 
461    dbg = AFNI_yesenv("AFNI_IMSAVE_DEBUG") ;  /* 03 Sep 2004 */
462 
463    /*-- path to open gimp [27 Oct 2017] --*/
464 
465    pg = THD_find_executable( "gimp" ) ;
466    if( pg != NULL ) gimp_path = strdup(pg) ;
467 #ifdef DARWIN  /* for MacosX, if in the App directory  */
468    else if( THD_is_directory("/Applications/GIMP.app") )
469      gimp_path = strdup("open -a /Applications/GIMP.app") ;
470    else if( THD_is_directory("/Applications/MacPorts/GIMP.app") )
471      gimp_path = strdup("open -a /Applications/MacPorts/GIMP.app") ;
472 #endif
473 
474    /*-- the cheap way to write PPM  --*/
475    /*-- [this must always be first] --*/
476 
477    pg = THD_find_executable( "cat" ) ;   /* should always find this! */
478    if( pg != NULL ){
479       str = AFMALL( char, strlen(pg)+32) ;
480       sprintf(str,"%s > %%s",pg) ;
481       bv <<= 1 ; ADDTO_PPMTO(str,"ppm",bv,0) ;
482 
483       /* 02 Aug 2001: also try for mpeg */
484 
485       ppmto_ppm_filter = str ;  /* save this filter string */
486 
487       pg = THD_find_executable( "ffmpeg" ) ;  /* ffmpeg replaces mpeg_encode [09 Dec 2019] */
488       if( pg != NULL ){
489          str = AFMALL( char, strlen(pg)+64) ;
490          sprintf(str,"%s %s",pg,FFMPEG_SUFFIX) ;
491          ppmto_mpeg_filter = str ;
492          if( dbg ) fprintf(stderr,"IMSAVE: animation filter '%s' for suffix '%s'\n",
493                            str , "mpg" ) ;
494       }
495       else CANT_FIND("ffmpeg","MPEG-1") ;
496    }
497    else CANT_FIND("cat","PPM") ;  /* this is the end of the world! */
498 
499    /*-- write JPEG --*/
500 
501    pg = THD_find_executable( "cjpeg" ) ;
502    if( pg != NULL ){
503    /* user environment variable compression quality - mod 5/10/2006 drg */
504       eee = my_getenv("AFNI_JPEG_COMPRESS");
505       if(eee!=NULL) {
506          jpeg_compress = (int) strtod(eee, NULL);
507          if((jpeg_compress<=5) || (jpeg_compress>100)) jpeg_compress = 95;
508       }
509       else jpeg_compress = 95;
510 
511 #if 0
512 printf("\njpeg_compress %d\n", jpeg_compress);
513 #endif
514       str = AFMALL( char, strlen(pg)+32) ;
515       sprintf(str,"%s -quality %d > %%s",pg,jpeg_compress);
516       bv <<= 1 ; ADDTO_PPMTO(str,"jpg",bv,1) ;
517       ppmto_jpg95_filter = strdup(str) ;  /* 28 Jul 2005 */
518 
519       /* lower quality JPEGs */
520 
521       ppmto_jpg75_filter = AFMALL( char, strlen(pg)+32);
522       sprintf(ppmto_jpg75_filter,"%s -quality 80 > %%s",pg) ;
523    }
524    else CANT_FIND("cjpeg","JPEG") ;
525 
526    /*-- write GIF --*/
527 
528    pg  = THD_find_executable( "ppmtogif" ) ;
529    pg2 = THD_find_executable( "ppmquant" ) ;
530    if( pg2 == NULL ) pg2 = THD_find_executable( "pnmquant" ) ;
531    if( pg != NULL && pg2 != NULL ){
532       int adel=20 ; char asuff[64] ;               /* 16 Jan 2003 */
533 
534       str = AFMALL( char, strlen(pg)+strlen(pg2)+32) ;
535       sprintf(str,"%s 255 | %s > %%s",pg2,pg) ;
536       bv <<= 1 ; ADDTO_PPMTO(str,"gif",bv,0) ;
537 
538       /*-- 27 Jul 2001: also try for Animated GIF --*/
539 
540       ppmto_gif_filter = str ;  /* save this filter string */
541 
542 #ifdef USE_GIFF                       /* filter for Fixed GIF colormap */
543       str = AFMALL( char , strlen(pg)+128 ) ;           /* 05 Oct 2004 */
544       sprintf(str,"%s -map %s > %%s",pg,GIFF_MAPFILE) ;
545       ppmto_giff_filter = str ;
546 #endif
547 
548       /* 16 Jan 2003: get animated GIF delay (centiseconds) from environment */
549 
550       eee = getenv( "AFNI_AGIF_DELAY" ) ;
551       if( eee != NULL ){ adel=(int)strtod(eee,NULL); if(adel < 2)adel=20; }
552 
553       pg = THD_find_executable( "gifsicle" ) ;    /* preferred */
554       if( pg != NULL ){
555          sprintf(asuff,GIFSICLE_SUFFIX,adel) ;    /* 16 Jan 2003 */
556          str = AFMALL( char, strlen(pg)+64) ;
557          sprintf(str,"%s %s",pg,asuff) ;
558          ppmto_agif_filter = str ;
559          if( dbg ) fprintf(stderr,"IMSAVE: animation filter '%s' for suffix '%s'\n",
560                            str , "gif" ) ;
561       } else {
562          pg = THD_find_executable( "whirlgif" ) ; /* but is OK */
563          if( pg != NULL ){
564             sprintf(asuff,WHIRLGIF_SUFFIX,adel) ; /* 16 Jan 2003 */
565             str = AFMALL( char, strlen(pg)+64) ;
566             sprintf(str,"%s %s",pg,asuff) ;
567             ppmto_agif_filter = str ;
568             if( dbg ) fprintf(stderr,"IMSAVE: animation filter '%s' for suffix '%s'\n",
569                               str , "gif" ) ;
570          }
571       }
572       if( ppmto_agif_filter == NULL )
573         CANT_FIND("gifsicle OR whirlgif","Animated GIF") ;
574    }
575    else { CANT_FIND("ppmtogif AND/OR ppmquant","GIF"); need_netpbm++; }
576 
577    /*-- write TIFF --*/
578 
579    pg = THD_find_executable( "ppm2tiff" ) ;
580    if( pg != NULL ){
581       str = AFMALL( char, strlen(pg)+32) ;
582       sprintf(str,"%s -c none %%s",pg) ;
583       bv <<= 1 ; ADDTO_PPMTO(str,"tif",bv,1) ;
584    } else {                                      /* 03 Jul 2001:      */
585       pg = THD_find_executable( "pnmtotiff" ) ;
586       if( pg == NULL )
587         pg = THD_find_executable( "pamtotiff" ); /* must use ppm2tiff */
588       if( pg != NULL ){                          /* and pnmtotiff     */
589          str = AFMALL( char, strlen(pg)+32) ;    /* differently       */
590          sprintf(str,"%s > %%s",pg) ;
591          bv <<= 1 ; ADDTO_PPMTO(str,"tif",bv,1) ;
592       }
593       else { CANT_FIND("ppm2tiff OR pnmtotiff OR pamtotiff","TIFF"); need_netpbm++; }
594    }
595 
596    /*-- write Windows BMP --*/
597 
598    pg  = THD_find_executable( "ppmtobmp" ) ;
599 
600    if( AFNI_yesenv("AFNI_OLD_PPMTOBMP") ){    /* the old way: quantize */
601      pg2 = THD_find_executable( "ppmquant" ) ;
602      if( pg != NULL && pg2 != NULL ){
603         str = AFMALL( char, strlen(pg)+strlen(pg2)+32) ;
604         sprintf(str,"%s 255 | %s -windows > %%s",pg2,pg) ;
605         bv <<= 1 ; ADDTO_PPMTO(str,"bmp",bv,0) ;
606      }
607      else { CANT_FIND("ppmtobmp AND/OR ppmquant","BMP"); need_netpbm++; }
608    } else if( pg != NULL ){                   /* 21 Feb 2003: don't quantize */
609       str = AFMALL( char, strlen(pg)+32) ;
610       sprintf(str,"%s -bpp 24 -windows > %%s",pg) ;
611       bv <<= 1 ; ADDTO_PPMTO(str,"bmp",bv,0) ;
612    }
613    else { CANT_FIND("ppmtobmp","BMP"); need_netpbm++; }
614 
615 #if 0
616    /*-- write Encapsulated PostScript [removed 02 Mar 2021] --*/
617 
618    pg = THD_find_executable( "pnmtops" ) ;
619    if( pg != NULL ){
620       str = AFMALL( char, strlen(pg)+32) ;
621       sprintf(str,"%s -noturn > %%s",pg) ;
622       bv <<= 1 ; ADDTO_PPMTO(str,"eps",bv,0) ;
623    }
624 #if 0
625    else { CANT_FIND("pnmtops","EPS"); need_netpbm++; }
626 #endif
627 #endif
628 
629 #if 0
630    /*-- write a PDF file (God only knows why) --*/
631 
632    pg2 = THD_find_executable( "epstopdf" ) ;   /* 19 Oct 2001:  */
633    if( pg != NULL && pg2 != NULL ){            /* check pg!=NULL */
634       str = AFMALL( char, strlen(pg)+strlen(pg2)+32) ;
635       sprintf(str,"%s -noturn | %s --filter > %%s",pg,pg2) ;
636       bv <<= 1 ; ADDTO_PPMTO(str,"pdf",bv,0) ;
637    }
638    else CANT_FIND("pnmtops AND/OR epstopdf","PDF") ;
639 #endif
640 
641    /*-- Write a PNG file --*/
642 
643    pg = THD_find_executable( "pnmtopng" ) ;
644    if( pg != NULL ){
645       str = AFMALL( char, strlen(pg)+32) ;
646       sprintf(str,"%s -compression 9 > %%s",pg) ;
647       bv <<= 1 ; ADDTO_PPMTO(str,"png",bv,1) ;
648       ppmto_png_filter = strdup(str) ;  /* 07 Dec 2007 */
649    }
650    else { CANT_FIND("pnmtopng","PNG"); need_netpbm++; }
651 
652    /*----- 16 Nov 2004: output more warnings? -----*/
653 
654    if( !AFNI_noenv("AFNI_IMSAVE_WARNINGS") && ncant > 0 ){
655 
656      if( need_netpbm > 0 ){  /* warnings for netpbm filters */
657        fprintf(stderr,"++\n") ;
658        fprintf(stderr,
659                "++ Some of the missing image Save programs are in\n"
660                "++  the netpbm software package, which is freeware.\n" ) ;
661        fprintf(stderr,"++\n") ;
662 #ifdef DARWIN
663        fprintf(stderr,
664                "++ Netpbm can be installed on MacOS X using one of these systems:\n"
665                "++    brew   https://brew.sh/\n"
666                "++    fink   https://www.finkproject.org/\n"
667                "++    ports  https://www.macports.org/\n"
668                "++ brew is the system currently recommended at the AFNI Mac installation page\n"
669                "++ https://afni.nimh.nih.gov/pub/dist/doc/htmldoc/background_install/install_instructs/steps_mac.html\n"
670               ) ;
671        fprintf(stderr,"++\n") ;
672 #endif
673      }
674      /* warnings for all problems */
675      fprintf(stderr,
676                "++ To disable these warnings, set environment\n"
677                "++  variable AFNI_IMSAVE_WARNINGS to 'NO'.\n"
678                "+++++++++++++++++++++++++++++++++++++++++++++\n" ) ;
679    }
680 
681    return ;
682 }
683 
684 /*-------------------------------------------------------------------------
685   routine to create a new window for displaying an image sequence:
686 
687   dc = pointer to a display context (MCW_DC)
688          (stores all the X11 stuff, like the Display *).
689 
690   get_image = pointer to a routine that returns the images to be displayed.
691 
692               - get_image(n,type,aux) should return a "MRI_IMAGE *" of the
693                  n-th image for n=0,1,...,nim-1, where "type" is one
694                  of isqCR_getimage (for the underlay image) or
695                     isqCR_getoverlay (for the overlay image);
696 
697               - get_image(n,isqCR_getstatus,aux) should return a
698                    "MCW_imseq_status *" (n is ignored)
699 
700               - get_image(n,isqCR_getmemplot,aux) should return a
701                    "MEM_plotdata *" (see coxplot.h -- 21 Feb 2001);
702                    NULL means no plot
703 
704               - get_image(n,isqCR_getlabel,aux) should return a
705                    "char *": a NUL-terminated string to be plotted
706                    on top of the image; NULL means no label string
707 
708             Thus, get_image takes as input 2 "int"s and an "XtPointer",
709             and returns a "XtPointer".  Note that the MRI_IMAGEs returned
710             will be mri_free-d after being used internally.  Therefore,
711             if you want to keep them, you should send a copy, not the
712             original.  The same applies to the MCW_imseq_status struct,
713             the MEM_plotdata struct, and the char *.
714 
715     aux = XtPointer supplied by user, pointing to data to be passed
716             get_image for its own internal use (similar in concept to
717             "client_data" for callbacks).
718 -------------------------------------------------------------------------*/
719 
720 /*-- structure defining buttons in main window --*/
721 
722 static const ISQ_bdef ISQ_but_bot_def[NBUTTON_BOT] = {  /* label, callback */
723      { "Disp"     , ISQ_but_disp_CB } ,
724      { "Save:bkg" , ISQ_but_save_CB } ,
725      { "Mont"     , ISQ_montage_CB  } ,
726      { "Done"     , ISQ_but_done_CB }
727 } ;
728 
729 static const RwcBoolean ISQ_but_bot_dial[NBUTTON_BOT] = {  /* use seq->dialog? */
730    True , False , True , False
731 } ;
732 
733 static char *ISQ_but_done_label1 = "Done" ;
734 static char *ISQ_but_done_label2 = "DONE" ;
735 #define NBUT_DONE (NBUTTON_BOT-1)
736 #define NBUT_SAVE 1
737 #define NBUT_DISP 0
738 #define NBUT_MONT 2
739 
740 static char *ISQ_save_label_bg  = "Save:bkg" ;
741 static char *ISQ_save_label_all = "Save:pnm" ;
742 
743 #define SET_SAVE_LABEL(seq)                                               \
744   do{ char sl[16] ;                                                       \
745       if( (seq)->opt.save_filter < 0 ){                                   \
746          strcpy(sl, (seq)->opt.save_pnm ? ISQ_save_label_all              \
747                                         : ISQ_save_label_bg );            \
748       }else{                                                              \
749          sprintf(sl,"Save.%.3s",ppmto_suffix[(seq)->opt.save_filter]) ;   \
750       }                                                                   \
751            if( (seq)->opt.save_agif ) strcpy(sl,"Sav:aGif") ;             \
752       else if( (seq)->opt.save_mpeg ) strcpy(sl,"Sav:mpeg") ;             \
753       else if( (seq)->opt.save_one  ) sl[3] = '1' ;                       \
754       MCW_set_widget_label( (seq)->wbut_bot[NBUT_SAVE] , sl ) ; } while(0)
755 
756 static const ISQ_bdef ISQ_but_rig_def[NBUTTON_RIG] = {
757      { "Colr" , ISQ_but_color_CB } ,
758      { "Swap" , ISQ_but_cswap_CB } ,
759      { "Norm" , ISQ_but_cnorm_CB }
760 } ;
761 
762 /* popup help for these buttons */
763 
764 static char * ISQ_but_bot_hint[NBUTTON_BOT] = {
765    "Extra image controls" ,
766    "Save images controls" ,
767    "Image montage controls" ,
768    "Close window"
769 } ;
770 
771 static char * ISQ_but_bot_help[NBUTTON_BOT] = {
772    "Pops up a window with options\n"
773    "to control the image display\n"
774    "and how the Save button works"   ,
775 
776    "Will popup control panels to let you save images from this window.\n"
777    "The type of save operation is indicated on the button label, and\n"
778    "is selected from the 'Disp' button options window.\n"
779    " :bkg = Will save only the background image data values\n"
780    "        (in a recorder window, the background image IS in color)\n"
781    " :pnm = Will save the actual displayed focus image in color (PNM format)\n"
782    "NOTES:\n"
783    " * Saved images will NOT be stretched to match window resizing.\n"
784    " * The PNM format requires the 'netpbm' package to be useful.\n"
785    "    Alternatively, the 'xv' program will read/write PNM images." ,
786 
787    "Will popup a control box to let you\n"
788    "display a montage of images, instead\n"
789    "of just one image at a time.\n\n"
790    "WARNING: this can be quite slow!"   ,
791 
792    "Closes this\n"
793    "viewing window"
794 } ;
795 
796 static char * ISQ_but_rig_help[NBUTTON_RIG] = {
797    "Switches the colormap\nbetween False Color\nand Grayscale" ,
798    "Swaps the colormap\nend for end" ,
799    "Restores the colormap\nto its `normal' state"
800 } ;
801 
802 static char * ISQ_but_rig_hint[NBUTTON_RIG] = {
803    "Switch between color and gray" ,
804    "Invert color/gray levels" ,
805    "Return color/gray scale to normal"
806 } ;
807 
808 static char * ISQ_scale_help =
809   "Moves between images:\nDrag bar, or click in trough" ;
810 
811 /* The next 2 helps will be overwritten by AFNI */
812 
813 static char * ISQ_default_image_help = "This is the image!" ;
814 
815 static char * ISQ_form_help =
816      "************************************************\n"
817      "* Image Sequence Display Module                *\n"
818      "*                                              *\n"
819      "* Released under the GPL (v2 or later)         *\n"
820      "*                                              *\n"
821      "* Author:  Robert W Cox, PhD                   *\n"
822      "************************************************"   ;
823 
824 /*-- arrow definitions for widgets down the right side of viewer --*/
825 
826 static char * ISQ_arrow_label[NARROW] = { "c" , "b" , "r" , "g" , "i" } ;
827 
828 #define NARR_SQUEEZE 0  /* arrow action codes */
829 #define NARR_BRIGHT  1
830 #define NARR_ROTATE  2
831 #define NARR_GAMMA   3
832 #define NARR_FRAC    4
833 
834 static char * ISQ_arrow_help[NARROW] = {
835    "Change constrast\nin colormap" ,
836    "Change brightness\nin colormap" ,
837    "Rotate\ncolormap" ,
838    "Alter\ndisplay\ngamma" ,
839    "Alter\nimage\nfraction\nin window"
840 } ;
841 
842 static char * ISQ_arrow_hint[NARROW] = {
843    "Contrast" ,
844    "Brightness" ,
845    "Rotate" ,
846    "Gamma" ,
847    "Image fraction"
848 } ;
849 
850 /*........................................................................*/
851 
852 static int ISQ_anim_dup = 0 ;
ISQ_set_anim_dup(int ii)853 void ISQ_set_anim_dup( int ii ){ ISQ_anim_dup = ii ; }
854 
855 /*........................................................................*/
856 
857 #define DEFAULT_MINFRAC 0.02
858 #define DEFAULT_MAXFRAC 0.90
859 
860 #define OPACITY_FAC  0.11111  /* 06-07 Mar 2001: overlay opacity stuff */
861 #define OPACITY_BOT  1
862 #define OPACITY_TOP  9
863 
864 #define ZOOM_BOT  1          /* 11 Mar 2002: zoom controls */
865 #define ZOOM_TOP  4
866 
open_MCW_imseq(MCW_DC * dc,get_ptr get_image,XtPointer aux)867 MCW_imseq * open_MCW_imseq( MCW_DC *dc ,
868                             get_ptr get_image , XtPointer aux )
869 {
870    MCW_imseq        *newseq ;
871    MCW_imseq_status *imstatus=NULL ;
872    int ii , xwide , yhigh , one_image ;
873    float fac ;
874    MRI_IMAGE *tim=NULL ;
875    float minfrac=DEFAULT_MINFRAC ; char *eee ; /* 27 Feb 2001 */
876    Widget wtemp ;                              /* 11 Mar 2002 */
877    float maxfrac=DEFAULT_MAXFRAC ;             /* 13 Jun 2003 */
878 
879 ENTRY("open_MCW_imseq") ;
880 
881 #define ERREX { myXtFree(newseq) ; XBell(dc->display,100) ; RETURN(NULL) ; }
882 
883    /*- 27 Jun 2001: setup filters for saving images -*/
884 
885    if( ppmto_num < 0 ){
886       ISQ_setup_ppmto_filters() ;  /* get filter program names */
887 
888       if( ppmto_num > 0 ){         /* modify Save button box setup for Disp */
889 
890          int nbut_old     = ISQ_dispbb[NTOG_SAV].nbut , qq,pp ;
891          char ** lbut_old = ISQ_dispbb[NTOG_SAV].lbut ;
892          char ** help_old = ISQ_bb_allhelp[NTOG_SAV] ;
893          char ** hint_old = ISQ_bb_allhint[NTOG_SAV] ;
894 
895          ISQ_dispbb[NTOG_SAV].nbut += ppmto_num ;
896          ISQ_dispbb[NTOG_SAV].lbut  = (char **) malloc(sizeof(char *)
897                                                        *ISQ_dispbb[NTOG_SAV].nbut);
898          for( qq=0 ; qq < nbut_old ; qq++ )
899             ISQ_dispbb[NTOG_SAV].lbut[qq] = lbut_old[qq] ;
900          for( pp=0 ; pp < ppmto_num ; pp++,qq++ ){
901             ISQ_dispbb[NTOG_SAV].lbut[qq] = AFMALL( char, 32) ;
902             sprintf(ISQ_dispbb[NTOG_SAV].lbut[qq] ,
903                     "Save to .%.3s(s)" , ppmto_suffix[pp] ) ;
904          }
905 
906          ISQ_bb_allhelp[NTOG_SAV] = (char **) malloc(sizeof(char *)
907                                                      *ISQ_dispbb[NTOG_SAV].nbut);
908          ISQ_bb_allhint[NTOG_SAV] = (char **) malloc(sizeof(char *)
909                                                      *ISQ_dispbb[NTOG_SAV].nbut);
910          for( qq=0 ; qq < nbut_old ; qq++ ){
911             ISQ_bb_allhelp[NTOG_SAV][qq] = help_old[qq] ;
912             ISQ_bb_allhint[NTOG_SAV][qq] = hint_old[qq] ;
913          }
914          for( pp=0 ; pp < ppmto_num ; pp++,qq++ )
915             ISQ_bb_allhelp[NTOG_SAV][qq] = ISQ_bb_allhint[NTOG_SAV][qq] = NULL ;
916       }
917    }
918 
919    /* Creation ex nihilo! */
920 
921    newseq = (MCW_imseq *) XtMalloc( sizeof(MCW_imseq) ) ;  /* new structure */
922    memset(newseq, 0, sizeof(MCW_imseq));
923 
924    newseq->dc     = dc ;               /* copy input pointers */
925    newseq->getim  = get_image ;
926    newseq->getaux = aux ;
927 
928    newseq->never_drawn = 1 ;           /* used when viewer is first opened */
929 
930    /*** The way the get_image function is called was changed
931         to use a macro that casts the arguments to the proper
932         types, to avoid bitter complaints from C++ compilers.
933         See Amalloc.h for these macros and other similar fixes. ***/
934 
935    /* get the status of the images to be supplied */
936 #if 0
937    imstatus = (MCW_imseq_status *) get_image(0,isqCR_getstatus,aux) ;
938 #else
939    AFNI_CALL_VALU_3ARG( get_image , MCW_imseq_status *,imstatus ,
940                         int,0 , int,isqCR_getstatus , XtPointer,aux ) ;
941 #endif
942    if( imstatus->num_total < 1 ){ ERREX ; }  /* bad */
943    one_image = (imstatus->num_total == 1) ;  /* special case */
944 
945   /* get the first image, for sizing purposes */
946 #if 0
947    tim = (MRI_IMAGE *) get_image(0,isqCR_getqimage,aux) ;  /* fake image */
948 #else
949    AFNI_CALL_VALU_3ARG( get_image , MRI_IMAGE *,tim ,
950                         int,0 , int,isqCR_getqimage , XtPointer,aux ) ;
951 #endif
952 
953    newseq->horig = tim->nx ;  /* save original dimensions */
954    newseq->vorig = tim->ny ;
955 
956    newseq->last_width_mm  = IM_WIDTH(tim) ;  /* dimensions in real space */
957    newseq->last_height_mm = IM_HEIGHT(tim) ;
958 
959    newseq->last_dx = newseq->last_dy = 1.0 ; /* 08 Jun 2004 */
960 
961    newseq->cropit       =  0 ; /* Cropping stuff - 11 Jun 2002 */
962    newseq->crop_allowed =  1 ;
963    newseq->crop_nxorg   = newseq->crop_nyorg = -1 ;
964    newseq->crop_autocenter = AFNI_yesenv("AFNI_CROP_AUTOCENTER") ;
965 
966    fac = (newseq->last_width_mm  / newseq->horig)    /* width per pixel over */
967         /(newseq->last_height_mm / newseq->vorig) ;  /* height per pixel */
968 
969    if( fac >= 1.0 ){                                 /* initial display size */
970       xwide = newseq->horig * fac + 0.49 ;
971       yhigh = newseq->vorig ;
972    } else {
973       xwide = newseq->horig ;
974       yhigh = newseq->vorig / fac + 0.49 ;
975    }
976 
977 if( PRINT_TRACING ){
978   char str[256] ;
979   sprintf(str,"nx=%d ny=%d dx=%f dy=%f wid=%f hei=%f xwide=%d yhigh=%d",
980               tim->nx,tim->ny,tim->dx,tim->dy,newseq->last_width_mm,
981               newseq->last_height_mm , xwide,yhigh ) ;
982   STATUS(str);
983 }
984 
985    KILL_1MRI(tim) ;  /* don't need tim no more */
986 
987    newseq->hbase  = newseq->hactual =
988                     newseq->old_hact = xwide ;   /* store display sizes */
989 
990    newseq->vbase  = newseq->vactual =
991                     newseq->old_vact = yhigh ;
992 
993    newseq->status = imstatus ;
994    newseq->im_nr  = imstatus->num_total / 2 ;  /* do middle image 1st */
995    newseq->scl    = 0.0 ;                      /* autoscaling */
996    newseq->lev    = dc->ncol_im-1 ;            /* to range 0..ncol_im-1 */
997    newseq->bot    = 0 ;
998    newseq->top    = dc->ncol_im-1 ;
999 
1000    newseq->clbot  = newseq->cltop  = 0.0f ;     /* 29 Jul 2001 */
1001    newseq->barbot = newseq->bartop = 0.0f ;
1002 
1003    /* color curve correction parameters */
1004    newseq->rgb_gamma  = 1.0 ;                /* 25 Apr 2005 */
1005    newseq->rgb_offset = 0.0 ;
1006 
1007    strcpy( newseq->im_label , "hi bob" ) ;
1008    newseq->scl_label[0] = '\0' ;
1009    newseq->overlay_label = NULL ;
1010 
1011    /* set display processing options */
1012 
1013    ISQ_DEFAULT_OPT(newseq->opt) ;  /* 09 Oct 1998: macro replaces explicit code */
1014    if( ppmto_num > 0 ) newseq->opt.save_filter = 0 ;  /* 26 Mar 2002 */
1015    newseq->opt.parent = (XtPointer) newseq ;
1016    newseq->old_opt    = newseq->opt ;      /* backup copy */
1017 
1018    newseq->last_image_type = -1 ;          /* not a legal datum type */
1019 
1020    newseq->dialog         = NULL ;         /* no dialog at present */
1021    newseq->num_bbox       = 0 ;            /* dialog = Disp, etc */
1022    newseq->dialog_starter = -1 ;
1023    newseq->dont_place_dialog = 0 ;         /* 23 Jan 2004 */
1024 
1025    newseq->imim = newseq->ovim = NULL ;    /* NULL out all stored images */
1026 
1027    newseq->orim      = NULL ;              /* original image - 30 Dec 1998 */
1028    newseq->set_orim  = 0 ;
1029    newseq->need_orim = 0 ;
1030 
1031    newseq->last_automask = NULL ;          /* 12 Dec 2014 */
1032 
1033    newseq->saver_blowup = 1 ;              /* image save blowup factor */
1034 
1035    newseq->given_xim = newseq->sized_xim   /* XImages for actual drawing */
1036                      = newseq->given_xbar  /* saved for re-use when possible */
1037                      = newseq->sized_xbar = NULL ;
1038 
1039    /* Feb 1998: button2 drawing stuff [enabled/used by Draw Dataset plugin] */
1040 
1041    newseq->button2_enabled  = 0 ;
1042    newseq->button2_active   = 0 ;
1043    newseq->button2_pixel    = dc->ovc->pixov_greenest ;
1044    newseq->button2_drawmode = BUTTON2_OPENPOLY ;
1045    newseq->button2_width    =  0 ;  /* 08 Oct 2002 */
1046    newseq->wimage_width     = -1 ;
1047    newseq->wimage_height    = -1 ;
1048 
1049    newseq->cursor_state     = CURSOR_NORMAL ;   /* 10 Mar 2003 */
1050 
1051    /* initialize image statistics */
1052 
1053    newseq->imstat = (ISQ_indiv_statistics *)
1054                     XtMalloc( sizeof(ISQ_indiv_statistics)
1055                               * imstatus->num_total ) ;
1056 
1057    /* the global statistics are no longer used */
1058 
1059    newseq->glstat = (ISQ_glob_statistics * )
1060                     XtMalloc( sizeof(ISQ_glob_statistics) ) ;
1061 
1062    for( ii=0 ; ii < imstatus->num_total ; ii++ ){
1063      newseq->imstat[ii].one_done = newseq->imstat[ii].glob_done = False ;
1064      newseq->imstat[ii].parent   = (XtPointer) newseq ;
1065    }
1066 
1067    newseq->glstat->parent = (XtPointer) newseq ;
1068 
1069    for( ii=0 ; ii < NHISTOG ; ii++ )
1070       newseq->glstat->hist[ii] = 0 ;  /* initialize */
1071 
1072    newseq->glstat->mm_done =
1073      newseq->glstat->per_done = (newseq->status->num_series < 2 ) ;
1074 
1075 #ifdef AUTOMATE_STATISTICS
1076    if( newseq->glstat->mm_done ){
1077       newseq->glstat->worker = 0 ;
1078    } else {
1079       newseq->glstat->worker = XtAppAddWorkProc(
1080                                   newseq->dc->appcontext ,
1081                                   ISQ_statistics_WP , newseq ) ;
1082    }
1083 #else
1084    newseq->glstat->worker = 0 ;
1085 #endif
1086 
1087    /***--------- create widgets ---------- ***/
1088 
1089    newseq->image_frac = IMAGE_FRAC ;  /* 25 Oct 1996 */
1090 
1091    /** 27 Feb 2001: set minimum size for image windows,
1092                     as a fraction of the overall screen area **/
1093 
1094    eee = my_getenv("AFNI_IMAGE_MINFRAC") ;
1095    if( eee != NULL ){
1096       float fff=0.0 ;
1097       ii = sscanf(eee,"%f",&fff) ;
1098       if( ii > 0 && fff > 0.0 && fff <= 0.9 ) minfrac = fff ;
1099       else                                    minfrac = DEFAULT_MINFRAC ;
1100    }
1101 
1102    eee = my_getenv("AFNI_IMAGE_MAXFRAC") ;
1103    if( eee != NULL ){
1104       float fff=0.0 ;
1105       ii = sscanf(eee,"%f",&fff) ;
1106       if( ii > 0 && fff > 0.0 && fff <= 1.0 ) maxfrac = fff ;
1107       else                                    maxfrac = DEFAULT_MAXFRAC ;
1108    }
1109 
1110    { float xxx = newseq->hactual , yyy = newseq->vactual ;
1111      float fff = (xxx*yyy)/(dc->width*dc->height) , ggg ;
1112 
1113      /* modify if window too small for display */
1114 
1115      if( fff < minfrac ){
1116        fff = sqrt(minfrac/fff); xxx *= fff; yyy *= fff;   /* expand area */
1117      }
1118 
1119      /* modify if window too big for display */
1120 
1121      fff = ggg = 1.0 ;
1122      if( xxx >= maxfrac*dc->width ) fff = maxfrac*dc->width / xxx; /* don't let  */
1123      if( yyy >= maxfrac*dc->height) ggg = maxfrac*dc->height/ yyy; /* be too big */
1124      fff = MIN(fff,ggg) ; xxx *= fff ; yyy *= fff ;
1125      if( xxx < 1.0 || yyy < 1.0 ){                     /* weird result?? */
1126        xxx = newseq->hactual ; yyy = newseq->vactual; /* back to old way */
1127      }
1128      xwide = (int) ( 0.49 + xxx / IMAGE_FRAC ) ;
1129      yhigh = (int) ( 0.49 + yyy / IMAGE_FRAC ) ;
1130 
1131      fff = ggg = 1.0 ;
1132      if( xwide >= maxfrac*dc->width ) fff = maxfrac*dc->width / xwide; /* don't let  */
1133      if( yhigh >= maxfrac*dc->height) ggg = maxfrac*dc->height/ yhigh; /* be too big */
1134      fff = MIN(fff,ggg) ; xwide *= fff ; yhigh *= fff ;
1135    }
1136 
1137    /* toggles for widget controls on or off */
1138 
1139    newseq->onoff_num   = 0 ;
1140    newseq->onoff_state = 1 ;  /* initially are on */
1141 
1142    /* top level shell to hold all */
1143 
1144    newseq->wtop =
1145       XtVaAppCreateShell(
1146            "AFNI" , "AFNI" ,
1147            topLevelShellWidgetClass , dc->display ,
1148 
1149            XmNminAspectX , xwide ,      /* fix aspect ratio! */
1150            XmNminAspectY , yhigh ,
1151            XmNmaxAspectX , xwide ,
1152            XmNmaxAspectY , yhigh ,
1153 
1154            XmNmaxWidth   , dc->width ,  /* not bigger than the screen! */
1155            XmNmaxHeight  , dc->height ,
1156 
1157            XmNdeleteResponse , XmDO_NOTHING , /* deletion handled below */
1158 
1159            XmNallowShellResize , False ,       /* let code resize shell */
1160 
1161 
1162            XmNinitialResourcesPersistent , False ,
1163       NULL ) ;
1164 
1165    DC_yokify( newseq->wtop , dc ) ;  /* 14 Sep 1998 */
1166 
1167 #if 1
1168    if( MCW_isitmwm( newseq->wtop ) )
1169       XtVaSetValues( newseq->wtop ,
1170                         XmNmwmDecorations , MWM_DECOR_ALL | MWM_DECOR_MAXIMIZE ,
1171                      NULL ) ;
1172 #endif
1173 
1174    XmAddWMProtocolCallback(           /* make "Close" window menu work */
1175            newseq->wtop ,
1176            XmInternAtom( dc->display , "WM_DELETE_WINDOW" , False ) ,
1177            ISQ_but_done_CB , newseq ) ;
1178 
1179    newseq->done_first = True ;  /* for the first press of "Done" */
1180 
1181    /* form to attach all contents to */
1182 
1183    newseq->wform =
1184       XtVaCreateWidget(
1185            "imseq" , xmFormWidgetClass , newseq->wtop ,
1186 
1187             XmNwidth  , xwide ,      /* initial size */
1188             XmNheight , yhigh ,
1189 
1190             XmNborderWidth , 0 ,
1191 
1192             XmNfractionBase , FORM_FRAC_BASE ,
1193 
1194             XmNhorizontalSpacing , 0 ,  /* 17 Jun 2002 */
1195             XmNverticalSpacing   , 0 ,
1196 
1197             XmNtraversalOn , False ,
1198             XmNinitialResourcesPersistent , False ,
1199       NULL ) ;
1200 
1201    MCW_register_help( newseq->wform , ISQ_form_help ) ;
1202 
1203 #define METER_HEIGHT 10
1204    newseq->render_scal =       /* will be hidden later */
1205      XtVaCreateManagedWidget(
1206             "menu" , xmScaleWidgetClass , newseq->wform ,
1207                XmNminimum , 0 ,
1208                XmNmaximum , 100 ,
1209                XmNscaleMultiple , 1 ,
1210                XmNshowValue , True ,
1211                XmNvalue , 50 ,
1212                XmNorientation , XmHORIZONTAL ,
1213                /** XmNscaleWidth , wid , **/
1214                XmNscaleHeight , METER_HEIGHT ,
1215                XmNborderWidth , 0 ,
1216                XmNhighlightThickness , 0 ,
1217                XmNshadowThickness , 0 ,
1218                XmNtraversalOn , True  ,
1219                XmNinitialResourcesPersistent , False ,
1220                XmNtopAttachment   , XmATTACH_FORM ,
1221                XmNleftAttachment  , XmATTACH_FORM ,
1222                XmNrightAttachment , XmATTACH_FORM ,
1223             NULL ) ;
1224    XtAddCallback( newseq->render_scal , XmNvalueChangedCallback ,
1225                   ISQ_render_scal_CB , newseq ) ;
1226    XtAddCallback( newseq->render_scal , XmNdragCallback ,
1227                   ISQ_render_scal_CB , newseq ) ;
1228 
1229    /* drawing area for image space */
1230 
1231    newseq->wimage =
1232        XtVaCreateManagedWidget(
1233          "imseq" , xmDrawingAreaWidgetClass , newseq->wform ,
1234 
1235           XmNtopAttachment    , XmATTACH_FORM ,
1236           XmNleftAttachment   , XmATTACH_FORM ,
1237           XmNrightAttachment  , XmATTACH_POSITION ,
1238           XmNrightPosition    , (int)( 0.49 + IMAGE_FRAC * FORM_FRAC_BASE ) ,
1239           XmNbottomAttachment , XmATTACH_POSITION ,
1240           XmNbottomPosition   , (int)( 0.49 + IMAGE_FRAC * FORM_FRAC_BASE ) ,
1241 
1242           XmNtraversalOn , False ,
1243           XmNinitialResourcesPersistent , False ,
1244        NULL ) ;
1245 
1246    XtInsertEventHandler( newseq->wimage ,      /* handle events in image */
1247 
1248                             0
1249                           | KeyPressMask        /* get keystrokes */
1250                           | ButtonPressMask     /* button presses */
1251                           | ExposureMask        /* exposures */
1252                           | StructureNotifyMask /* resizes (Configure events) */
1253                           | Button1MotionMask   /* motion while #1 is down */
1254                           | ButtonReleaseMask   /* button releases */
1255                          ,
1256                          FALSE ,                /* nonmaskable events? */
1257                          ISQ_drawing_EV ,       /* super-handler! */
1258                          (XtPointer) newseq ,   /* client data */
1259                          XtListTail ) ;         /* last in queue */
1260 
1261    strcpy( newseq->im_helptext , ISQ_default_image_help ) ;
1262    newseq->im_helptext[ISQ_NHELP] = '\0' ;
1263 
1264    MCW_register_help( newseq->wimage , newseq->im_helptext ) ;
1265 
1266    /* all pushbuttons (these are next so they overlay the scale and bar) */
1267 
1268    for( ii=0 ; ii < NBUTTON_BOT ; ii++){
1269 
1270       Arg wa[30] ;
1271       int na ;
1272 
1273       na = 0 ;
1274 
1275       XtSetArg( wa[na] , XmNmarginWidth   , 1     ) ; na++ ;
1276       XtSetArg( wa[na] , XmNmarginHeight  , 0     ) ; na++ ;
1277       XtSetArg( wa[na] , XmNmarginBottom  , 0     ) ; na++ ;
1278       XtSetArg( wa[na] , XmNmarginTop     , 0     ) ; na++ ;
1279       XtSetArg( wa[na] , XmNmarginLeft    , 0     ) ; na++ ;
1280       XtSetArg( wa[na] , XmNmarginRight   , 0     ) ; na++ ;
1281       XtSetArg( wa[na] , XmNtraversalOn   , False ) ; na++ ;
1282       XtSetArg( wa[na] , XmNrecomputeSize , False ) ; na++ ;
1283 
1284       XtSetArg( wa[na] , XmNinitialResourcesPersistent , False ) ; na++ ;
1285 
1286       /* attach all buttons to edge of form */
1287 
1288       XtSetArg( wa[na] , EDGING_BOT , XmATTACH_FORM ) ; na++ ;
1289 
1290       if( ii == 0 ){  /* attach 1st button to leading edge of form */
1291 
1292          XtSetArg( wa[na] , LEADING_BOT , XmATTACH_FORM ) ; na++ ;
1293 
1294       } else if( ii == NBUTTON_BOT-1 ){  /* last button */
1295 
1296          XtSetArg(wa[na],LEADING_BOT       ,XmATTACH_WIDGET)        ; na++ ;
1297          XtSetArg(wa[na],LEADING_WIDGET_BOT,newseq->wbut_bot[ii-1]) ; na++ ;
1298 
1299       } else {  /* other buttons to the widget to their LEADING edge */
1300 
1301          XtSetArg(wa[na],LEADING_BOT       ,XmATTACH_WIDGET )       ; na++ ;
1302          XtSetArg(wa[na],LEADING_WIDGET_BOT,newseq->wbut_bot[ii-1] ); na++ ;
1303       }
1304 
1305       newseq->onoff_widgets[(newseq->onoff_num)++] =
1306       newseq->wbut_bot[ii] =
1307          XtCreateManagedWidget(
1308                ISQ_but_bot_def[ii].name ,
1309                xmPushButtonWidgetClass , newseq->wform ,
1310                wa , na ) ;
1311 
1312       XtAddCallback( newseq->wbut_bot[ii] , XmNactivateCallback ,
1313                      ISQ_but_bot_def[ii].func_CB , newseq ) ;
1314 
1315       MCW_register_help( newseq->wbut_bot[ii] , ISQ_but_bot_help[ii] ) ;
1316       MCW_register_hint( newseq->wbut_bot[ii] , ISQ_but_bot_hint[ii] ) ;
1317 
1318    } /* end of loop over bottom buttons */
1319    SET_SAVE_LABEL(newseq) ;
1320 
1321    MCW_set_widget_bg( newseq->wbut_bot[NBUT_DONE] ,
1322                       MCW_hotcolor(newseq->wbut_bot[0]) , 0 ) ;
1323 
1324    /* 27 Jun 2001: popup menu for Save: button */
1325 
1326    if( ppmto_num > 0 )
1327      XtInsertEventHandler( newseq->wbut_bot[NBUT_SAVE] ,
1328                            ButtonPressMask ,    /* button presses */
1329                            FALSE ,              /* nonmaskable events? */
1330                            ISQ_butsave_EV ,     /* handler */
1331                            (XtPointer) newseq , /* client data */
1332                            XtListTail           /* last in queue */
1333                           ) ;
1334 
1335    /* 17 Jun 2011: Button3 actions for Disp button */
1336 
1337    XtInsertEventHandler( newseq->wbut_bot[NBUT_DISP] ,
1338                          ButtonPressMask ,    /* button presses */
1339                          FALSE ,              /* nonmaskable events? */
1340                          ISQ_butdisp_EV ,     /* handler */
1341                          (XtPointer) newseq , /* client data */
1342                          XtListTail           /* last in queue */
1343                         ) ;
1344 
1345    /* 24 Apr 2001: initialize recording stuff */
1346 
1347    ISQ_record_button( newseq ) ;
1348 
1349    /* buttons on right */
1350 
1351    STATUS("creating buttons on right") ;
1352    for( ii=0 ; ii < NBUTTON_RIG ; ii++){
1353 
1354       Arg wa[30] ;
1355       int na ;
1356 
1357       na = 0 ;
1358 
1359       XtSetArg( wa[na] , XmNmarginWidth   , 1     ) ; na++ ;
1360       XtSetArg( wa[na] , XmNmarginHeight  , 0     ) ; na++ ;
1361       XtSetArg( wa[na] , XmNmarginBottom  , 0     ) ; na++ ;
1362       XtSetArg( wa[na] , XmNmarginTop     , 0     ) ; na++ ;
1363       XtSetArg( wa[na] , XmNmarginLeft    , 0     ) ; na++ ;
1364       XtSetArg( wa[na] , XmNmarginRight   , 0     ) ; na++ ;
1365       XtSetArg( wa[na] , XmNtraversalOn   , False ) ; na++ ;
1366       XtSetArg( wa[na] , XmNrecomputeSize , False ) ; na++ ;
1367 
1368       XtSetArg( wa[na] , XmNinitialResourcesPersistent , False ) ; na++ ;
1369 
1370       /* attach all buttons to edge of form */
1371 
1372       XtSetArg( wa[na] , EDGING_RIG , XmATTACH_FORM ) ; na++ ;
1373 
1374       if( ii == 0 ){  /* attach 1st button to leading edge of form */
1375 
1376          XtSetArg( wa[na] , LEADING_RIG , XmATTACH_FORM ) ; na++ ;
1377 
1378       } else {  /* other buttons to the widget to their LEADING edge */
1379 
1380          XtSetArg(wa[na],LEADING_RIG       ,XmATTACH_WIDGET        ); na++ ;
1381          XtSetArg(wa[na],LEADING_WIDGET_RIG,newseq->wbut_rig[ii-1] ); na++ ;
1382       }
1383 
1384       newseq->onoff_widgets[(newseq->onoff_num)++] =
1385       newseq->wbut_rig[ii] =
1386          XtCreateManagedWidget(
1387                ISQ_but_rig_def[ii].name ,
1388                xmPushButtonWidgetClass , newseq->wform ,
1389                wa , na ) ;
1390 
1391       XtAddCallback( newseq->wbut_rig[ii] , XmNactivateCallback ,
1392                      ISQ_but_rig_def[ii].func_CB , newseq ) ;
1393 
1394       MCW_register_help( newseq->wbut_rig[ii] , ISQ_but_rig_help[ii] ) ;
1395       MCW_register_hint( newseq->wbut_rig[ii] , ISQ_but_rig_hint[ii] ) ;
1396    }
1397 
1398    /* arrows on right */
1399 
1400    STATUS("creating arrows on right") ;
1401    for( ii=0 ; ii < NARROW ; ii++ ){
1402 
1403       newseq->arrow[ii] = new_MCW_arrowval(
1404                              newseq->wform , ISQ_arrow_label[ii] ,
1405                              MCW_AV_downup , 0,0,0 ,
1406                              MCW_AV_notext , 0 ,
1407                              ISQ_arrow_CB , (XtPointer) newseq ,
1408                              NULL,NULL ) ;
1409 
1410       newseq->onoff_widgets[(newseq->onoff_num)++] = newseq->arrow[ii]->wrowcol ;
1411 
1412       XtVaSetValues( newseq->arrow[ii]->wrowcol ,
1413                         EDGING_RIG   , XmATTACH_FORM ,
1414                         LEADING_RIG  , XmATTACH_WIDGET ,
1415 
1416                         LEADING_WIDGET_RIG ,
1417                            (ii==0) ? (newseq->wbut_rig[NBUTTON_RIG-1])
1418                                    : (newseq->arrow[ii-1]->wrowcol) ,
1419                      NULL ) ;
1420 
1421       if( ii != NARR_FRAC )
1422          newseq->arrow[ii]->fastdelay = 10 ;                 /* fast */
1423       newseq->arrow[ii]->parent       = (XtPointer) newseq ; /* set parent */
1424 
1425       MCW_reghelp_children( newseq->arrow[ii]->wrowcol, ISQ_arrow_help[ii] );
1426       MCW_reghint_children( newseq->arrow[ii]->wrowcol, ISQ_arrow_hint[ii] );
1427    }
1428 
1429    /** 07 Mar 2001 - add opacity control for overlay, maybe **/
1430 
1431    wtemp = newseq->arrow[NARROW-1]->wrowcol ;  /* 11 Mar 2002 */
1432 
1433    newseq->ov_opacity = 1.0 ;  /* 06 Mar 2001 */
1434 
1435    if( newseq->dc->visual_class == TrueColor ){
1436      int iov = (int)rint(newseq->ov_opacity/OPACITY_FAC) ;
1437      char *buf = ISQ_opacity_label(iov) ;
1438 
1439      /** 08 Mar 2001 - put a line between the arrows above and this control **/
1440 
1441      STATUS("creating opacity control") ;
1442      newseq->ov_opacity_sep = XtVaCreateManagedWidget(
1443                                 "imseq" , xmSeparatorWidgetClass , newseq->wform ,
1444                                    XmNseparatorType , XmSINGLE_LINE ,
1445                                    EDGING_RIG   , XmATTACH_FORM ,
1446                                    LEADING_RIG  , XmATTACH_WIDGET ,
1447                                    LEADING_WIDGET_RIG , wtemp ,
1448                                    XmNleftAttachment , XmATTACH_OPPOSITE_WIDGET ,
1449                                    XmNleftWidget , wtemp ,
1450                                    XmNleftOffset , 7 ,
1451                                 NULL ) ;
1452      newseq->onoff_widgets[(newseq->onoff_num)++] = newseq->ov_opacity_sep ;
1453 
1454      newseq->ov_opacity_av = new_MCW_arrowval(
1455                                newseq->wform ,        /* parent */
1456                                buf ,                  /* label */
1457                                MCW_AV_downup ,        /* direction */
1458                                OPACITY_BOT ,          /* min */
1459                                OPACITY_TOP ,          /* max */
1460                                iov ,                  /* init */
1461                                MCW_AV_notext ,        /* type */
1462                                0 ,                    /* decim */
1463                                ISQ_opacity_CB ,       /* action CB */
1464                                (XtPointer) newseq ,   /* and its data */
1465                                NULL ,                 /* text maker */
1466                                NULL                   /* and its data */
1467                              ) ;
1468 
1469      newseq->ov_opacity_av->parent = (XtPointer) newseq ;
1470      newseq->onoff_widgets[(newseq->onoff_num)++] = newseq->ov_opacity_av->wrowcol ;
1471 
1472      XtVaSetValues( newseq->ov_opacity_av->wrowcol ,
1473                       EDGING_RIG   , XmATTACH_FORM ,
1474                       LEADING_RIG  , XmATTACH_WIDGET ,
1475                       LEADING_WIDGET_RIG , newseq->ov_opacity_sep ,
1476                     NULL ) ;
1477 
1478      wtemp = newseq->ov_opacity_av->wrowcol ; /* 11 Mar 2002 */
1479 
1480      MCW_reghelp_children( newseq->ov_opacity_av->wrowcol,
1481                            "Controls the opacity\n"
1482                            "of the color overlay:\n"
1483                            "  1 = barely visible  \n"
1484                            "  9 = totally opaque"   ) ;
1485      MCW_reghint_children( newseq->ov_opacity_av->wrowcol, "Color overlay opacity" );
1486 
1487    } else {
1488      newseq->ov_opacity_av  = NULL ;
1489      newseq->ov_opacity_sep = NULL ;
1490    }
1491 
1492    STATUS("creating zoom control") ;
1493      newseq->zoom_sep = XtVaCreateManagedWidget(
1494                          "imseq" , xmSeparatorWidgetClass , newseq->wform ,
1495                             XmNseparatorType , XmSINGLE_LINE ,
1496                             EDGING_RIG   , XmATTACH_FORM ,
1497                             LEADING_RIG  , XmATTACH_WIDGET ,
1498                             LEADING_WIDGET_RIG , wtemp ,
1499                             XmNleftAttachment , XmATTACH_OPPOSITE_WIDGET ,
1500                             XmNleftWidget , wtemp ,
1501                             XmNleftOffset , 7 ,
1502                          NULL ) ;
1503      newseq->onoff_widgets[(newseq->onoff_num)++] = newseq->zoom_sep ;
1504 
1505      newseq->zoom_val_av = new_MCW_arrowval(
1506                                newseq->wform ,        /* parent */
1507                                "z" ,                  /* label */
1508                                MCW_AV_downup ,        /* direction */
1509                                ZOOM_BOT ,             /* min */
1510                                ZOOM_TOP ,             /* max */
1511                                ZOOM_BOT ,             /* init */
1512                                MCW_AV_notext ,        /* type */
1513                                0 ,                    /* decim */
1514                                ISQ_zoom_av_CB ,       /* action CB */
1515                                (XtPointer) newseq ,   /* and its data */
1516                                NULL ,                 /* text maker */
1517                                NULL                   /* and its data */
1518                              ) ;
1519      newseq->zoom_val_av->parent = (XtPointer) newseq ;
1520      newseq->onoff_widgets[(newseq->onoff_num)++] = newseq->zoom_val_av->wrowcol ;
1521      XtVaSetValues( newseq->zoom_val_av->wrowcol ,
1522                       EDGING_RIG   , XmATTACH_FORM ,
1523                       LEADING_RIG  , XmATTACH_WIDGET ,
1524                       LEADING_WIDGET_RIG , newseq->zoom_sep ,
1525                     NULL ) ;
1526      MCW_reghelp_children( newseq->zoom_val_av->wrowcol,
1527                            "- Images can be zoomed in by\n"
1528                            "   a factor of 2, 3, or 4.\n"
1529                            "- These 'Z' buttons change\n"
1530                            "   the zoom factor up and down.\n"
1531                            "- Panning the zoomed image is\n"
1532                            "   done by pressing the 'pan'\n"
1533                            "   button and then clicking and\n"
1534                            "   dragging with Button #1 down\n\n"
1535                            "**WARNING: zooming in by 4 can\n"
1536                            "   consume so much memory that\n"
1537                            "   AFNI or the X11 server will\n"
1538                            "   crash.  If this happens, then\n"
1539                            "   avoid zooming that much (duh).\n" ) ;
1540      MCW_reghint_children( newseq->zoom_val_av->wrowcol, "Image zoom factor" );
1541      AV_SENSITIZE_DOWN( newseq->zoom_val_av , False ) ;
1542 
1543      STATUS("creating pan control") ;
1544      newseq->zoom_drag_pb =
1545         XtVaCreateManagedWidget(
1546            "imseq" , xmPushButtonWidgetClass , newseq->wform ,
1547             LABEL_ARG("pan") ,
1548             XmNmarginWidth   , 1 ,
1549             XmNmarginHeight  , 0 ,
1550             XmNmarginBottom  , 0 ,
1551             XmNmarginTop     , 0 ,
1552             XmNmarginLeft    , 0 ,
1553             XmNmarginRight   , 0 ,
1554             XmNtraversalOn , False ,
1555             XmNinitialResourcesPersistent , False ,
1556          NULL ) ;
1557      XtAddCallback( newseq->zoom_drag_pb , XmNactivateCallback ,
1558                     ISQ_zoom_pb_CB , newseq ) ;
1559 
1560      newseq->onoff_widgets[(newseq->onoff_num)++] = newseq->zoom_drag_pb ;
1561 
1562      XtVaSetValues( newseq->zoom_drag_pb ,
1563                       EDGING_RIG   , XmATTACH_FORM ,
1564                       LEADING_RIG  , XmATTACH_WIDGET ,
1565                       LEADING_WIDGET_RIG , newseq->zoom_val_av->wrowcol ,
1566                   NULL ) ;
1567 
1568      MCW_register_help( newseq->zoom_drag_pb ,
1569                            "To pan the zoomed image window:\n"
1570                            "- Click on this 'pan' button\n"
1571                            "- Then drag the image with mouse\n"
1572                            "   Button #1 (the cursor in the\n"
1573                            "   image window will be hand-shaped)\n"
1574                            "- When you finish dragging, panning\n"
1575                            "   mode will be turned off\n"
1576                            "- If you want panning mode to stay\n"
1577                            "   turned on until you click 'pan'\n"
1578                            "   again, set environment variable\n"
1579                            "   AFNI_KEEP_PANNING to YES"         ) ;
1580      MCW_register_hint( newseq->zoom_drag_pb ,
1581                            "Pan zoomed image" );
1582 
1583      SENSITIZE( newseq->zoom_drag_pb , 0 ) ;
1584 
1585      wtemp = newseq->zoom_drag_pb ;
1586 
1587      newseq->zoom_fac     = 1   ;     /* initialize data for zooming */
1588      newseq->zoom_hor_off = 0.0 ;
1589      newseq->zoom_ver_off = 0.0 ;
1590      newseq->zoom_pixmap  = (Pixmap) 0 ;
1591      newseq->zoom_pw      = 0 ;
1592      newseq->zoom_ph      = 0 ;
1593      newseq->zoom_xim     = NULL ;
1594      newseq->zoom_button1 = 0 ;       /* 15 Mar 2002 */
1595 
1596      /* 17 Jun 2002: crop pushbutton */
1597 
1598      STATUS("creating crop control") ;
1599      newseq->crop_drag_pb =
1600         XtVaCreateManagedWidget(
1601            "imseq" , xmPushButtonWidgetClass , newseq->wform ,
1602             LABEL_ARG("crop") ,
1603             XmNmarginWidth   , 1 ,
1604             XmNmarginHeight  , 0 ,
1605             XmNmarginBottom  , 0 ,
1606             XmNmarginTop     , 0 ,
1607             XmNmarginLeft    , 0 ,
1608             XmNmarginRight   , 0 ,
1609             XmNtraversalOn , False ,
1610             XmNinitialResourcesPersistent , False ,
1611          NULL ) ;
1612      XtAddCallback( newseq->crop_drag_pb , XmNactivateCallback ,
1613                     ISQ_crop_pb_CB , newseq ) ;
1614      newseq->crop_drag = 0 ;
1615 
1616      newseq->onoff_widgets[(newseq->onoff_num)++] = newseq->crop_drag_pb ;
1617 
1618      XtVaSetValues( newseq->crop_drag_pb ,
1619                       EDGING_RIG   , XmATTACH_FORM ,
1620                       LEADING_RIG  , XmATTACH_WIDGET ,
1621                       LEADING_WIDGET_RIG , wtemp ,
1622                   NULL ) ;
1623 
1624      MCW_register_help( newseq->crop_drag_pb ,
1625                            "To crop the image window:\n"
1626                            "- Click on the 'crop' button;\n"
1627                            "- Then click and hold down mouse Button #1\n"
1628                            "   at a corner of the rectangle to crop;\n"
1629                            "- Then drag a rectangle to set the crop size,\n"
1630                            "   and then release the mouse button.\n"
1631                            "\n"
1632                            "To uncrop (back to original image size):\n"
1633                            "- Click on the 'crop' button;\n"
1634                            "- Click on it again without cropping.\n"
1635                            "\n"
1636                            "* Another way to crop without using the 'crop'\n"
1637                            "  button is to drag the crop rectangle using\n"
1638                            "  Shift+Button #2.\n"
1639                            "\n"
1640                            "* Another way to uncrop is to click Shift+Button #2\n"
1641                            "  in the image without any dragging.\n"
1642                            "\n"
1643                            "* Button #3 (right-click) on the 'crop' pushbutton\n"
1644                            "  will popup a menu that lets you set the crop\n"
1645                            "  rectangle size exactly.\n"
1646                            "\n"
1647                            "* Scroll the crop region using Shift+keyboard arrows.\n"
1648                            "* Resize the crop region using Ctrl+keyboard arrows."
1649                        ) ;
1650      MCW_register_hint( newseq->crop_drag_pb , "Crop image" );
1651 
1652      XtInsertEventHandler( newseq->crop_drag_pb ,
1653                            ButtonPressMask ,    /* button presses */
1654                            FALSE ,              /* nonmaskable events? */
1655                            ISQ_butcrop_EV ,     /* handler */
1656                            (XtPointer) newseq , /* client data */
1657                            XtListTail           /* last in queue */
1658                           ) ;
1659 
1660      wtemp = newseq->crop_drag_pb ;
1661 
1662    /*-- Label for other info at right [11 Mar 2020] --*/
1663 
1664    newseq->rinfo_sep = XtVaCreateManagedWidget(
1665                          "imseq" , xmSeparatorWidgetClass , newseq->wform ,
1666                             XmNseparatorType , XmSINGLE_LINE ,
1667                             EDGING_RIG   , XmATTACH_FORM ,
1668                             LEADING_RIG  , XmATTACH_WIDGET ,
1669                             LEADING_WIDGET_RIG , wtemp ,
1670                             XmNleftAttachment , XmATTACH_OPPOSITE_WIDGET ,
1671                             XmNleftWidget , wtemp ,
1672                             XmNleftOffset , 7 ,
1673                             XmNrecomputeSize , True ,
1674                          NULL ) ;
1675    wtemp = newseq->onoff_widgets[(newseq->onoff_num)++] = newseq->rinfo_sep ;
1676 
1677    newseq->onoff_widgets[(newseq->onoff_num)++] =
1678    newseq->rinfo = XtVaCreateManagedWidget(
1679                      "font7" , xmLabelWidgetClass , newseq->wform ,
1680                        EDGING_RIG   , XmATTACH_FORM ,
1681                        LEADING_RIG  , XmATTACH_WIDGET ,
1682                        LEADING_WIDGET_RIG , wtemp ,
1683                        XmNinitialResourcesPersistent , False ,
1684                      NULL ) ;
1685    BLACK_AND_WHITE_WIDGET(newseq->rinfo) ;
1686    strcpy(newseq->rinfo_label,"OK") ;
1687    MCW_register_hint( newseq->rinfo ,
1688                       "Dataset axes: Card=parallel to LR-AP-IS; Obliq=not parallel" ) ;
1689    MCW_register_help( newseq->rinfo ,
1690                       " \n"
1691                       "This label shows the underlay dataset axes\n"
1692                       "orientation with respect to anatomical\n"
1693                       "xyz axis (L-R, A-P, I-S):\n"
1694                       "  Card = dataset parallel with anatomical xyz\n"
1695                       " Obliq = dataset NOT parallel\n"
1696                       "         in which case the default AFNI image\n"
1697                       "         viewers won't show the underlay\n"
1698                       "         slices in the 'standard' cut planes.\n " ) ;
1699    wtemp = newseq->rinfo ;
1700 
1701    /* 18 Jul 2003: toggle button for pen (drawing mode) */
1702 
1703    { char *lbl = "pen" ;
1704      STATUS("creating pen control") ;
1705      newseq->pen_bbox = new_MCW_bbox( newseq->wform ,
1706                                       1 , &lbl ,
1707                                       MCW_BB_check , MCW_BB_noframe ,
1708                                       ISQ_pen_bbox_CB , (XtPointer)newseq ) ;
1709 
1710      newseq->onoff_widgets[(newseq->onoff_num)++] = newseq->pen_bbox->wrowcol ;
1711 
1712      XtVaSetValues( newseq->pen_bbox->wrowcol ,
1713                       EDGING_RIG   , XmATTACH_FORM ,
1714                       LEADING_RIG  , XmATTACH_WIDGET ,
1715                       LEADING_WIDGET_RIG , wtemp ,
1716                   NULL ) ;
1717 
1718      MCW_reghelp_children( newseq->pen_bbox->wrowcol ,
1719                            "In ROI drawing mode, toggles\n"
1720                            "the cursor to a pen shape,\n"
1721                            "and turn on drawing with\n"
1722                            "mouse Button-1."
1723                          ) ;
1724      MCW_reghint_children( newseq->pen_bbox->wrowcol ,
1725                            "Toggle pen drawing" ) ;
1726 
1727      XtUnmanageChild( newseq->pen_bbox->wrowcol ) ;
1728      wtemp = newseq->pen_bbox->wrowcol ;
1729    }
1730 
1731    /* scale for image number */
1732 
1733    ii = (one_image) ? 1 : newseq->status->num_total - 1 ;
1734 
1735    STATUS("creating image scale") ;
1736    newseq->onoff_widgets[(newseq->onoff_num)++] =
1737    newseq->wscale =
1738        XtVaCreateManagedWidget(
1739           "imseq" , xmScaleWidgetClass , newseq->wform ,
1740 
1741           XmNtopAttachment    , XmATTACH_WIDGET ,
1742           XmNtopWidget        , newseq->wimage ,
1743           XmNleftAttachment   , XmATTACH_FORM ,
1744           XmNrightAttachment  , XmATTACH_POSITION ,
1745           XmNrightPosition    , (int)( 0.49 + IMAGE_FRAC * FORM_FRAC_BASE ),
1746 
1747           XmNminimum       , 0 ,                       /* range of scale */
1748           XmNmaximum       , ii ,
1749           XmNvalue         , newseq->im_nr ,           /* initial image */
1750           XmNshowValue     , True ,                    /* show image num */
1751           XmNscaleMultiple , 1 ,                       /* single step */
1752           XmNorientation   , XmHORIZONTAL ,            /* sideways */
1753 
1754           XmNtraversalOn , False ,
1755           XmNinitialResourcesPersistent , False ,
1756        NULL ) ;
1757 
1758    XtAddCallback( newseq->wscale , XmNvalueChangedCallback ,
1759                   ISQ_scale_CB , newseq ) ;
1760 
1761    MCW_reghelp_children( newseq->wscale , ISQ_scale_help ) ;
1762 #if 0
1763    MCW_register_hint( newseq->wscale , "Moves between images" ) ;
1764 #endif
1765 
1766    /* arrowpad at lower right corner */
1767 
1768    STATUS("creating arrowpad") ;
1769    newseq->arrowpad = new_MCW_arrowpad(
1770                            newseq->wform ,
1771                            ISQ_arrowpad_CB , (XtPointer) newseq ) ;
1772 
1773    newseq->onoff_widgets[(newseq->onoff_num)++] = newseq->arrowpad->wform ;
1774 
1775    XtVaSetValues( newseq->arrowpad->wform ,
1776                      XmNbottomAttachment , XmATTACH_FORM ,
1777                      XmNrightAttachment  , XmATTACH_FORM ,
1778                      XtNmappedWhenManaged , False ,   /* managed later */
1779                   NULL ) ;
1780 
1781    newseq->arrowpad->parent = (XtPointer) newseq ;
1782 
1783    /* drawing area for color bar */
1784 
1785    STATUS("creating intensity bar") ;
1786    newseq->onoff_widgets[(newseq->onoff_num)++] =
1787    newseq->wbar =
1788        XtVaCreateManagedWidget(
1789           "imseq" , xmDrawingAreaWidgetClass , newseq->wform ,
1790 
1791            XmNtopAttachment    , XmATTACH_FORM ,
1792            XmNleftAttachment   , XmATTACH_WIDGET ,
1793            XmNleftWidget       , newseq->wimage ,
1794            XmNleftOffset       , COLOR_BAR_SPACE ,
1795            XmNbottomAttachment , XmATTACH_POSITION ,
1796            XmNbottomPosition   , (int)( 0.49 + IMAGE_FRAC * FORM_FRAC_BASE ),
1797 
1798            XmNwidth       , COLOR_BAR_WIDTH ,
1799 
1800            XmNtraversalOn , False ,
1801            XmNinitialResourcesPersistent , False ,
1802        NULL ) ;
1803 
1804    XtInsertEventHandler( newseq->wbar ,          /* handle events in bar */
1805 
1806                             0
1807                           | ButtonPressMask      /* button presses */
1808                           | ExposureMask         /* exposures */
1809                           | StructureNotifyMask  /* resizes (Configure events) */
1810                          ,
1811                          FALSE ,                 /* nonmaskable events? */
1812                          ISQ_drawing_EV ,        /* super-handler! */
1813                          (XtPointer) newseq ,    /* client data */
1814                          XtListTail ) ;          /* last in queue */
1815 
1816    /* popup menu on wbar */
1817 
1818    MCW_register_help( newseq->wbar ,
1819                       "Use Button 3 to popup\n"
1820                       "a display control menu\n"
1821                       "\n"
1822                       "Use Button 1 to enforce\n"
1823                       "image aspect ratio"       ) ;
1824 
1825    STATUS("creating intensity bar menu") ;
1826 
1827 #ifdef BAD_BUTTON3_POPUPS   /* 21 Jul 2003 */
1828    newseq->wbar_menu = XmCreatePopupMenu( newseq->wscale, "menu",NULL,0 ) ;
1829 #else
1830    newseq->wbar_menu = XmCreatePopupMenu( newseq->wbar  , "menu",NULL,0 ) ;
1831 #endif
1832 
1833    SAVEUNDERIZE(XtParent(newseq->wbar_menu)) ;  /* 27 Feb 2001 */
1834 
1835    VISIBILIZE_WHEN_MAPPED(newseq->wbar_menu) ;
1836 #if 0
1837    if( !AFNI_yesenv("AFNI_DISABLE_TEAROFF") ) TEAROFFIZE(newseq->wbar_menu) ;
1838 #else
1839    (void) XtVaCreateManagedWidget(
1840             "dialog" , xmPushButtonWidgetClass , newseq->wbar_menu ,
1841                LABEL_ARG("--- Cancel ---") ,
1842                XmNrecomputeSize , False ,
1843                XmNtraversalOn , False ,
1844                XmNinitialResourcesPersistent , False ,
1845             NULL ) ;
1846 #endif
1847 
1848    newseq->wbar_rng_but =
1849       XtVaCreateManagedWidget(
1850          "menu" , xmPushButtonWidgetClass , newseq->wbar_menu ,
1851             LABEL_ARG("Choose Display Range") ,
1852             XmNtraversalOn , False ,
1853             XmNinitialResourcesPersistent , False ,
1854          NULL ) ;
1855    XtAddCallback( newseq->wbar_rng_but, XmNactivateCallback, ISQ_wbar_menu_CB, newseq ) ;
1856    MCW_register_hint(newseq->wbar_rng_but,"Fix bot,top values for underlay") ;
1857 
1858    newseq->wbar_zer_but =
1859       XtVaCreateManagedWidget(
1860          "menu" , xmPushButtonWidgetClass , newseq->wbar_menu ,
1861             LABEL_ARG("Choose Zero Color") ,
1862             XmNtraversalOn , False ,
1863             XmNinitialResourcesPersistent , False ,
1864          NULL ) ;
1865    XtAddCallback( newseq->wbar_zer_but, XmNactivateCallback, ISQ_wbar_menu_CB, newseq ) ;
1866    MCW_register_hint(newseq->wbar_zer_but,"Color for zero value in underlay") ;
1867 
1868    { char *blab[1] = { "Automask?" } ;
1869      newseq->wbar_amask_bbox = new_MCW_bbox( newseq->wbar_menu ,  /* 14 Jun 2010 */
1870                                              1 , blab ,
1871                                              MCW_BB_check , MCW_BB_noframe ,
1872                                              ISQ_wbar_amask_CB , (XtPointer)newseq ) ;
1873      MCW_reghint_children(newseq->wbar_amask_bbox->wrowcol,"Automatically zero out image exterior") ;
1874    }
1875 
1876    { char *blab[1] = { "Invert?" } ;
1877      newseq->wbar_invrt_bbox = new_MCW_bbox( newseq->wbar_menu ,  /* 14 Jun 2010 */
1878                                              1 , blab ,
1879                                              MCW_BB_check , MCW_BB_noframe ,
1880                                              ISQ_wbar_invrt_CB , (XtPointer)newseq ) ;
1881      MCW_reghint_children(newseq->wbar_invrt_bbox->wrowcol,"Invert contrast?") ;
1882    }
1883 
1884 
1885    newseq->wbar_flat_but =
1886       XtVaCreateManagedWidget(
1887          "menu" , xmPushButtonWidgetClass , newseq->wbar_menu ,
1888             LABEL_ARG("Choose Flatten Range") ,
1889             XmNtraversalOn , False ,
1890             XmNinitialResourcesPersistent , False ,
1891          NULL ) ;
1892    XtAddCallback( newseq->wbar_flat_but, XmNactivateCallback, ISQ_wbar_menu_CB, newseq ) ;
1893 
1894    newseq->wbar_sharp_but =
1895       XtVaCreateManagedWidget(
1896          "menu" , xmPushButtonWidgetClass , newseq->wbar_menu ,
1897             LABEL_ARG("Choose Sharpen factor") ,
1898             XmNtraversalOn , False ,
1899             XmNinitialResourcesPersistent , False ,
1900          NULL ) ;
1901    XtAddCallback( newseq->wbar_sharp_but, XmNactivateCallback, ISQ_wbar_menu_CB, newseq ) ;
1902 
1903    newseq->wbar_vgize_but =
1904       XtVaCreateManagedWidget(
1905          "menu" , xmPushButtonWidgetClass , newseq->wbar_menu ,
1906             LABEL_ARG("Choose VG factor") ,
1907             XmNtraversalOn , False ,
1908             XmNinitialResourcesPersistent , False ,
1909          NULL ) ;
1910    XtAddCallback( newseq->wbar_vgize_but, XmNactivateCallback, ISQ_wbar_menu_CB, newseq ) ;
1911 
1912    newseq->rng_bot   = newseq->rng_top = newseq->rng_ztop = 0 ;
1913    newseq->flat_bot  = newseq->flat_top = 0.0 ;
1914    newseq->sharp_fac = 0.60f ; newseq->rng_extern = 0 ;
1915    newseq->vgize_fac = INDEX_TO_VGFAC(2) ;
1916 
1917    newseq->zer_color = 0 ;
1918    ii = DC_find_closest_overlay_color( newseq->dc ,
1919                                        getenv("AFNI_IMAGE_ZEROCOLOR") ) ;
1920    if( ii > 0 ) newseq->zer_color = ii ;
1921 
1922    { char *blab[1] = { "Crop Autocenter?" } ;
1923      newseq->wbar_crop_bbox = new_MCW_bbox( newseq->wbar_menu ,  /* 15 Jan 2014 */
1924                                              1 , blab ,
1925                                              MCW_BB_check , MCW_BB_noframe ,
1926                                              ISQ_wbar_crop_CB , (XtPointer)newseq ) ;
1927      MCW_reghint_children(newseq->wbar_crop_bbox->wrowcol,"Automatically center crop window on crosshairs") ;
1928      if( newseq->crop_autocenter ) MCW_set_bbox( newseq->wbar_crop_bbox , 1 ) ;
1929    }
1930 
1931 
1932    /* label for informational display */
1933 
1934    newseq->onoff_widgets[(newseq->onoff_num)++] =
1935    newseq->winfo = XtVaCreateManagedWidget(
1936                      "font7" , xmLabelWidgetClass , newseq->wform ,
1937                         XmNtopAttachment   , XmATTACH_WIDGET ,
1938                         XmNtopWidget       , newseq->wscale ,
1939                         XmNleftAttachment  , XmATTACH_FORM ,
1940                         XmNrightAttachment , XmATTACH_POSITION ,
1941                         XmNrightPosition   ,
1942                             (int)( 0.49 + IMAGE_FRAC * FORM_FRAC_BASE ) ,
1943                         XmNrecomputeSize   , False ,
1944                         XmNalignment       , XmALIGNMENT_END ,
1945                         XmNinitialResourcesPersistent , False ,
1946                      NULL ) ;
1947    LABELIZE(newseq->winfo) ;
1948    newseq->winfo_extra[0] = '\0' ;  /* 07 Aug 1999 */
1949 
1950    newseq->winfo_sides[0][0] =
1951     newseq->winfo_sides[1][0] =
1952      newseq->winfo_sides[2][0] =
1953       newseq->winfo_sides[3][0] = '\0' ; /* 01 Dec 1999 */
1954    newseq->winfo_prefix[0] = '\0' ; /* 10 Dec 2007 */
1955 
1956    /***---------- all widgets now created ------------***/
1957 
1958    newseq->mont_across_av   = NULL ;
1959    newseq->mont_down_av     = NULL ;
1960    newseq->mont_skip_av     = NULL ;
1961    newseq->mont_gap_av      = NULL ;
1962    newseq->mont_gapcolor_av = NULL ;
1963    newseq->mont_type_av     = NULL ;
1964 
1965    newseq->mont_nx       = newseq->mont_nx_old       = 1 ;
1966    newseq->mont_ny       = newseq->mont_ny_old       = 1 ;
1967    newseq->mont_skip     = newseq->mont_skip_old     = 0 ;
1968    newseq->mont_gap      = newseq->mont_gap_old      = 0 ;
1969    newseq->mont_gapcolor = newseq->mont_gapcolor_old = 0 ;
1970    newseq->mont_periodic = 1 ;                             /* default = periodic */
1971    newseq->mont_mode     = MONT_SPATIAL ;
1972 
1973 STATUS("creation: widgets created") ;
1974 
1975    XtManageChild( newseq->wform ) ;
1976 
1977 #if 0
1978    XtRealizeWidget( newseq->wtop ) ;
1979    newseq->valid = 2 ;  /* mark this structure as ready to roll */
1980 
1981    NORMAL_cursorize( newseq->wtop ) ;
1982 
1983 #else
1984    newseq->valid = 1 ;  /* mark this structure as valid but not realized */
1985 #endif
1986 
1987    newseq->ignore_redraws = 0 ;
1988 
1989    /* 30 Oct 1996 -- transformations */
1990 
1991    newseq->transform0D_func  = NULL ;  /* no function to start with */
1992    newseq->transform0D_av    = NULL ;
1993    newseq->transform0D_index = 0 ;
1994 
1995    newseq->transform2D_func  = NULL ;  /* no function to start with */
1996    newseq->transform2D_av    = NULL ;
1997    newseq->transform2D_index = 0 ;
1998 
1999    newseq->slice_proj_av       = NULL ;  /* 31 Jan 2002 */
2000    newseq->slice_proj_func     = NULL ;
2001    newseq->slice_proj_index    = 0    ;
2002    newseq->slice_proj_range_av = NULL ;
2003    newseq->slice_proj_range    = 0    ;
2004 
2005    newseq->rowgraph_av  = NULL ;       /* 30 Dec 1998 */
2006    newseq->rowgraph_num = 0 ;
2007    newseq->rowgraph_mtd = NULL ;
2008 
2009    newseq->graymap_mtd  = NULL ;       /* 24 Oct 2003 */
2010    newseq->cmap_changed = 0 ;
2011 
2012    newseq->shft_ctrl_dragged = 0 ;     /* 17 Mar 2010 */
2013 
2014 #define DEFAULT_THETA  55.0
2015 #define DEFAULT_PHI   285.0
2016 
2017    newseq->surfgraph_av    = NULL ;    /* 21 Jan 1999 */
2018    newseq->surfgraph_num   = 0    ;
2019    newseq->surfgraph_mtd   = NULL ;
2020    newseq->surfgraph_theta = DEFAULT_THETA ;
2021    newseq->surfgraph_phi   = DEFAULT_PHI   ;
2022    newseq->surfgraph_arrowpad = NULL ;
2023 
2024    newseq->mplot = NULL ;              /* 19 Sep 2001 */
2025 
2026    /* 20 Sep 2001: add a button box to control plots
2027                    and arrowvals to control labels   */
2028 
2029    { static char *plabel[1] = { "Plot Overlay Plots" } ;
2030      static char *alabel[7] = { "Off", "UpperLeft", "UpperRight",
2031                                        "LowerLeft", "LowerRight",
2032                                        "UpperMid" , "LowerMid"   } ;
2033      static char *slabel[6] = { "Tiny" , "Small" , "Medium" , "Large" , "Huge" , "Enormous" } ;
2034      static char *mlabel[3] = { "Slice", "Volume", "Dataset" };
2035 
2036      char *eee ; int iii ;
2037 
2038      (void) XtVaCreateManagedWidget( "menu",
2039                                      xmSeparatorWidgetClass, newseq->wbar_menu,
2040                                        XmNseparatorType , XmSINGLE_LINE ,
2041                                      NULL ) ;
2042 
2043 #if 1
2044      iii = THD_get_image_globalrange();
2045      if( iii < 0 || iii > 3 ) iii = 0 ;
2046 
2047      newseq->wbar_globrange_av =
2048         new_MCW_arrowval( newseq->wbar_menu ,
2049                           "Image Global Range" ,
2050                           MCW_AV_optmenu ,      /* option menu style */
2051                           0 ,                   /* first option */
2052                           2 ,                   /* last option */
2053                           iii ,                 /* initial selection */
2054                           MCW_AV_readtext ,     /* ignored but needed */
2055                           0 ,                   /* ditto */
2056                           ISQ_wbar_globrange_CB , /* callback when changed */
2057                           (XtPointer)newseq ,   /* data for above */
2058                           MCW_av_substring_CB , /* text creation routine */
2059                           mlabel                /* data for above */
2060                         ) ;
2061      MCW_reghint_children(newseq->wbar_globrange_av->wrowcol,
2062                   "Set how images are scaled in display - sets AFNI_IMAGE_GLOBALRANGE") ;
2063 #endif
2064 
2065      /*-- plots stuff --*/
2066 
2067      newseq->wbar_plots_bbox = new_MCW_bbox( newseq->wbar_menu ,
2068                                              1 , plabel ,
2069                                              MCW_BB_check , MCW_BB_noframe ,
2070                                              ISQ_wbar_plots_CB , (XtPointer)newseq ) ;
2071      MCW_set_bbox( newseq->wbar_plots_bbox , 1 ) ;
2072      MCW_reghint_children(newseq->wbar_plots_bbox->wrowcol,"Allow line drawing overlay stuff") ;
2073 
2074      newseq->wbar_graymap_pb =
2075         XtVaCreateManagedWidget(
2076            "menu" , xmPushButtonWidgetClass , newseq->wbar_menu ,
2077               LABEL_ARG("Display Graymap Plot") ,
2078               XmNtraversalOn , False ,
2079               XmNinitialResourcesPersistent , False ,
2080            NULL ) ;
2081      XtAddCallback( newseq->wbar_graymap_pb, XmNactivateCallback, ISQ_wbar_menu_CB, newseq ) ;
2082      MCW_register_hint(newseq->wbar_graymap_pb,"Graph intensity vs underlay image value") ;
2083 
2084      (void) XtVaCreateManagedWidget( "menu",
2085                                      xmSeparatorWidgetClass, newseq->wbar_menu,
2086                                        XmNseparatorType , XmSINGLE_LINE ,
2087                                      NULL ) ;
2088 
2089      newseq->timer_id = 0 ;  /* 03 Dec 2003 */
2090 
2091      newseq->render_mode = RENDER_DEFAULT ;  /* 0 */
2092      newseq->render_fac  = 0.0f ;            /* 22 Aug 2014 */
2093      newseq->allowmerger = 0 ;
2094 
2095      /*-- labels stuff --*/
2096 
2097      iii = 0 ;
2098      eee = getenv("AFNI_IMAGE_LABEL_MODE") ;
2099      if( eee != NULL ){
2100        iii = strtol(eee,NULL,10) ; if( iii < 0 || iii > 6 ) iii = 1 ;
2101      }
2102      newseq->wbar_label_av =
2103         new_MCW_arrowval( newseq->wbar_menu ,
2104                           "Label" ,
2105                           MCW_AV_optmenu ,      /* option menu style */
2106                           0 ,                   /* first option */
2107                           6 ,                   /* last option */
2108                           iii ,                 /* initial selection */
2109                           MCW_AV_readtext ,     /* ignored but needed */
2110                           0 ,                   /* ditto */
2111                           ISQ_wbar_label_CB ,   /* callback when changed */
2112                           (XtPointer)newseq ,   /* data for above */
2113                           MCW_av_substring_CB , /* text creation routine */
2114                           alabel                /* data for above */
2115                         ) ;
2116      MCW_reghint_children(newseq->wbar_label_av->wrowcol,"Show coordinate label") ;
2117 
2118      iii = 2 ;
2119      eee = getenv("AFNI_IMAGE_LABEL_SIZE") ;
2120      if( eee != NULL ){
2121         iii = strtol(eee,NULL,10) ; if( iii < 0 || iii > 5 ) iii = 2 ;
2122      }
2123      newseq->wbar_labsz_av =
2124         new_MCW_arrowval( newseq->wbar_menu ,
2125                           "Size " ,
2126                           MCW_AV_optmenu ,      /* option menu style */
2127                           0 ,                   /* first option */
2128                           5 ,                   /* last option */
2129                           iii ,                 /* initial selection */
2130                           MCW_AV_readtext ,     /* ignored but needed */
2131                           0 ,                   /* ditto */
2132                           ISQ_wbar_label_CB ,   /* callback when changed */
2133                           (XtPointer)newseq ,   /* data for above */
2134                           MCW_av_substring_CB , /* text creation routine */
2135                           slabel                /* data for above */
2136                         ) ;
2137      MCW_reghint_children(newseq->wbar_labsz_av->wrowcol,"Set coordinate label size") ;
2138 
2139      newseq->wbar_labst_pb =
2140         XtVaCreateManagedWidget(
2141            "menu" , xmPushButtonWidgetClass , newseq->wbar_menu ,
2142               LABEL_ARG("Label Append String") ,
2143               XmNtraversalOn , False ,
2144               XmNinitialResourcesPersistent , False ,
2145            NULL ) ;
2146      XtAddCallback( newseq->wbar_labst_pb, XmNactivateCallback, ISQ_wbar_menu_CB, newseq ) ;
2147      MCW_register_hint(newseq->wbar_labst_pb,"Set string to append to overlay label") ;
2148 
2149    } /* end of plots & labels stuff */
2150 
2151    /** 23 Feb 2003: menu items to control tic marks */
2152 
2153    (void) XtVaCreateManagedWidget( "menu",
2154                                    xmSeparatorWidgetClass, newseq->wbar_menu,
2155                                      XmNseparatorType , XmSINGLE_LINE ,
2156                                    NULL ) ;
2157    newseq->wbar_ticnum_av =
2158       new_MCW_arrowval( newseq->wbar_menu ,
2159                         "Tick Div." ,
2160                         MCW_AV_optmenu ,      /* option menu style */
2161                         0 ,                   /* first option */
2162                         21 ,                  /* last option */
2163                         0 ,                   /* initial selection */
2164                         MCW_AV_readtext ,     /* ignored but needed */
2165                         0 ,                   /* ditto */
2166                         ISQ_wbar_label_CB ,   /* callback when changed */
2167                         (XtPointer)newseq ,   /* data for above */
2168                         NULL                , /* text creation routine */
2169                         NULL                  /* data for above */
2170                       ) ;
2171    AVOPT_columnize(newseq->wbar_ticnum_av,2) ;
2172    MCW_reghint_children( newseq->wbar_ticnum_av->wrowcol ,
2173     "Number of tick marks on image edges [cf. AFNI_IMAGE_TICK_DIV_IN_MM]" ) ;
2174    newseq->wbar_ticsiz_av =
2175       new_MCW_arrowval( newseq->wbar_menu ,
2176                         "Tick Size" ,
2177                         MCW_AV_optmenu ,      /* option menu style */
2178                         1 ,                   /* first option */
2179                         10 ,                  /* last option */
2180                         1 ,                   /* initial selection */
2181                         MCW_AV_readtext ,     /* ignored but needed */
2182                         0 ,                   /* ditto */
2183                         ISQ_wbar_label_CB ,   /* callback when changed */
2184                         (XtPointer)newseq ,   /* data for above */
2185                         NULL                , /* text creation routine */
2186                         NULL                  /* data for above */
2187                       ) ;
2188    AVOPT_columnize(newseq->wbar_ticsiz_av,2) ;
2189    MCW_reghint_children( newseq->wbar_ticsiz_av->wrowcol ,
2190                          "Size of tick marks around image edges" ) ;
2191 
2192    /** 27 Oct 2008: menu item to control checkerboarding */
2193 
2194    (void) XtVaCreateManagedWidget( "menu",
2195                                    xmSeparatorWidgetClass, newseq->wbar_menu,
2196                                      XmNseparatorType , XmSINGLE_LINE ,
2197                                    NULL ) ;
2198    newseq->wbar_checkbrd_av =
2199       new_MCW_arrowval( newseq->wbar_menu ,
2200                         "CheckBrd#" ,
2201                         MCW_AV_optmenu ,      /* option menu style */
2202                         0 ,                   /* first option */
2203                         43 ,                  /* last option */
2204                         0 ,                   /* initial selection */
2205                         MCW_AV_readtext ,     /* ignored but needed */
2206                         0 ,                   /* ditto */
2207                         ISQ_wbar_label_CB ,   /* callback when changed */
2208                         (XtPointer)newseq ,   /* data for above */
2209                         NULL                , /* text creation routine */
2210                         NULL                  /* data for above */
2211                       ) ;
2212    AVOPT_columnize(newseq->wbar_checkbrd_av,4) ;
2213    MCW_reghint_children( newseq->wbar_checkbrd_av->wrowcol ,
2214                          "Size of checks in the checkerboard display [# key]" );
2215 
2216    /** 10 Feb 2009: menu item to control animation duplicates **/
2217 
2218    newseq->wbar_animdup_av =
2219       new_MCW_arrowval( newseq->wbar_menu ,
2220                         "Anim_Dup " ,
2221                         MCW_AV_optmenu ,      /* option menu style */
2222                         0 ,                   /* first option */
2223                         19 ,                  /* last option */
2224                         ISQ_anim_dup ,        /* initial selection */
2225                         MCW_AV_readtext ,     /* ignored but needed */
2226                         0 ,                   /* ditto */
2227                         ISQ_wbar_label_CB ,   /* callback when changed */
2228                         (XtPointer)newseq ,   /* data for above */
2229                         NULL                , /* text creation routine */
2230                         NULL                  /* data for above */
2231                       ) ;
2232    AVOPT_columnize(newseq->wbar_animdup_av,2) ;
2233    MCW_reghint_children( newseq->wbar_animdup_av->wrowcol ,
2234                          "Duplicate images for Save:aGif and Save:mpeg" ) ;
2235 
2236    newseq->top_clip = 0.0f ; /* 17 Sep 2007 */
2237    newseq->redo_clip = 0 ;
2238 
2239    /* 23 Jan 2003: set default save? */
2240 
2241    drive_MCW_imseq( newseq , isqDR_setimsave ,
2242                     (XtPointer)getenv("AFNI_DEFAULT_IMSAVE") ) ;
2243 
2244    /* 23 Jan 2003: set opacity? */
2245 
2246    { int opval = (int)AFNI_numenv("AFNI_DEFAULT_OPACITY") ;
2247      if( opval > 0 && opval <= 9 )
2248        drive_MCW_imseq( newseq , isqDR_setopacity , (XtPointer)ITOP(opval) ) ;
2249    }
2250 
2251    newseq->parent = NULL ;
2252 
2253    { static int first=1 ;
2254      if( first ){
2255        memplot_topshell_setsaver( ".jpg" , memplot_to_jpg ) ; /* 05 Dec 2007 */
2256        memplot_topshell_setsaver( ".png" , memplot_to_png ) ;
2257        first = 0 ;
2258      }
2259    }
2260 
2261    /* set scrollwheel threshold modifier mask [14 Feb 2014] */
2262 
2263    { char *eee = my_getenv("AFNI_IMAGE_SCROLLWHEEL_TMASK") ;
2264      int swt = 0 ;
2265      if( eee != NULL ){
2266        if( strcasestr(eee,"shift")   != NULL ) swt |= ShiftMask ;
2267        if( strcasestr(eee,"shft")    != NULL ) swt |= ShiftMask ;
2268        if( strcasestr(eee,"ctrl")    != NULL ) swt |= ControlMask ;
2269        if( strcasestr(eee,"control") != NULL ) swt |= ControlMask ;
2270        if( strcasestr(eee,"mod1")    != NULL ) swt |= Mod1Mask ;
2271        if( strcasestr(eee,"mod2")    != NULL ) swt |= Mod2Mask ;
2272        if( strcasestr(eee,"mod3")    != NULL ) swt |= Mod3Mask ;
2273        if( strcasestr(eee,"mod4")    != NULL ) swt |= Mod4Mask ;
2274        if( strcasestr(eee,"mod5")    != NULL ) swt |= Mod5Mask ;
2275 
2276        if( strcasestr(eee,"debug")   != NULL ) scrollwheel_debug = 1 ;
2277      }
2278      scrollwheel_tmask = (swt == 0) ? SWL_TMASK_DEFAULT : swt ;
2279    }
2280 
2281    XtUnmanageChild(newseq->render_scal) ;
2282    RETURN(newseq) ;
2283 }
2284 
2285 /*----------------------------------------------------------------------
2286    set the image dimensions in "physical units"
2287      (based on the dx and dy fields of the input image);
2288    the goal is to keep the same scaling from pixels -> mm even
2289    if the image is a different size
2290 ------------------------------------------------------------------------*/
2291 
ISQ_reset_dimen(MCW_imseq * seq,float new_width_mm,float new_height_mm)2292 void ISQ_reset_dimen( MCW_imseq *seq,  float new_width_mm, float new_height_mm )
2293 {
2294    int xwide , yhigh , oldx,oldy ;
2295    float scale_x , scale_y ;
2296    int wx,hy,xx,yy ;   /* geometry of shell */
2297    int xp,yp ;
2298    MCW_DC *dc ;
2299 
2300    float minfrac=DEFAULT_MINFRAC ; char *eee ; /* 12 Jun 2002 */
2301    float maxfrac=DEFAULT_MAXFRAC ;
2302 
2303 ENTRY("ISQ_reset_dimen") ;
2304 
2305    if( ! ISQ_VALID(seq) ) EXRETURN ;
2306 
2307    MCW_widget_geom( seq->wimage , &oldx , &oldy , NULL,NULL ) ;
2308 
2309    scale_x = seq->last_width_mm / oldx ;  /* mm/pixel as displayed now */
2310    scale_y = seq->last_height_mm/ oldy ;
2311 
2312    if( ! seq->opt.free_aspect ){                      /* fixed aspect */
2313       scale_x = scale_y = sqrt( scale_x * scale_y ) ; /*  means use   */
2314    }                                                  /* same scales! */
2315 
2316    xwide = new_width_mm / scale_x + 0.5 ;  /* so scale to new # of pixels */
2317    yhigh = new_height_mm/ scale_y + 0.5 ;
2318 
2319    /** 12 Jun 2002: set minimum size for image windows,
2320                     as a fraction of the overall screen area **/
2321 
2322    eee = my_getenv("AFNI_IMAGE_MINFRAC") ;
2323    if( eee != NULL ){
2324       float fff=0.0 ; int ii ;
2325       ii = sscanf(eee,"%f",&fff) ;
2326       if( ii > 0 && fff > 0.0 && fff <= 1.0 ) minfrac = fff ;
2327       else                                    minfrac = DEFAULT_MINFRAC ;
2328    }
2329 
2330    eee = my_getenv("AFNI_IMAGE_MAXFRAC") ;
2331    if( eee != NULL ){
2332       float fff=0.0 ; int ii ;
2333       ii = sscanf(eee,"%f",&fff) ;
2334       if( ii > 0 && fff > 0.0 && fff <= 1.0 ) maxfrac = fff ;
2335       else                                    maxfrac = DEFAULT_MAXFRAC ;
2336    }
2337 
2338    dc = seq->dc ;
2339 
2340    { float xxx = xwide , yyy = yhigh ;
2341      float fff = (xxx*yyy)/(dc->width*dc->height) , ggg ;
2342 
2343      /* modify if window too small */
2344 
2345      if( fff < minfrac ){
2346        fff = sqrt(minfrac/fff) ; xxx *= fff ; yyy *= fff ; /* expand area */
2347      }
2348 
2349      /* modify if window too big */
2350 
2351      fff = ggg = 1.0 ;
2352      if( xxx >= maxfrac*dc->width ) fff = maxfrac*dc->width / xxx ; /* don't let  */
2353      if( yyy >= maxfrac*dc->height) ggg = maxfrac*dc->height/ yyy ; /* be too big */
2354      fff = MIN(fff,ggg) ; xxx *= fff ; yyy *= fff ;
2355      if( xxx < 1.0 || yyy < 1.0 ){                      /* weird result?? */
2356         xxx = xwide ; yyy = yhigh ;                    /* back to old way */
2357      }
2358 
2359      xwide = (int)( 0.49 + xxx ) ;
2360      yhigh = (int)( 0.49 + yyy ) ;
2361    }
2362 
2363 if( PRINT_TRACING ){
2364   char str[256] ;
2365   sprintf(str,"last wid=%f hei=%f  new wid=%f hei=%f",
2366           seq->last_width_mm,seq->last_height_mm,new_width_mm,new_height_mm ) ;
2367   STATUS(str) ;
2368   sprintf(str,"new xwide=%d yhigh=%d  scale_x=%f _y=%f",
2369           xwide,yhigh,scale_x,scale_y) ;
2370   STATUS(str) ;
2371 }
2372 
2373    seq->last_width_mm  = new_width_mm ;
2374    seq->last_height_mm = new_height_mm ;
2375 
2376    /* possibly expand to include control widgets (if they are on) */
2377 
2378    if( seq->onoff_state ){
2379      float fff,ggg ;
2380      xwide = (int) ( 0.49 + xwide / seq->image_frac ) ;  /* new size of shell */
2381      yhigh = (int) ( 0.49 + yhigh / seq->image_frac ) ;
2382 
2383      fff = ggg = 1.0 ;
2384      if( xwide >= maxfrac*dc->width ) fff = maxfrac*dc->width /xwide; /* 13 Jun 2003  */
2385      if( yhigh >= maxfrac*dc->height) ggg = maxfrac*dc->height/yhigh; /* Fri the 13th */
2386      fff = MIN(fff,ggg) ;
2387      fff = MIN(fff,ggg) ; xwide *= fff ; yhigh *= fff ;
2388    }
2389 
2390    if( seq->opt.free_aspect ){
2391       XtVaSetValues( seq->wtop ,
2392                        XmNminAspectX ,  1 ,   /* free up aspect ratio */
2393                        XmNminAspectY , 20 ,
2394                        XmNmaxAspectX , 20 ,
2395                        XmNmaxAspectY ,  1 ,
2396                      NULL ) ;
2397    } else {
2398       XtVaSetValues( seq->wtop ,
2399                        XmNminAspectX , xwide ,   /* reset aspect ratio */
2400                        XmNminAspectY , yhigh ,
2401                        XmNmaxAspectX , xwide ,
2402                        XmNmaxAspectY , yhigh ,
2403                      NULL ) ;
2404    }
2405 
2406    XtVaSetValues( seq->wtop ,
2407                      XmNwidth  , xwide ,      /* reset size of form */
2408                      XmNheight , yhigh ,
2409                   NULL ) ;
2410 
2411    /* it is possible that the image has flipped off the screen now -- fix that! */
2412 
2413    MCW_widget_geom( seq->wtop , &wx,&hy,&xx,&yy ) ;
2414 
2415    if( xx+wx/2 < 1 ) xp = 10 ; else xp = xx ;
2416    if( yy+hy/2 < 1 ) yp = 10 ; else yp = yy ;
2417 
2418    if( xp != xx || yp != yy )
2419      XtVaSetValues( seq->wtop , XmNx , xp , XmNy , yp , NULL ) ;
2420 
2421    /* if there is a dialog, move it too [modified 05 Jan 1999] */
2422 
2423    if( seq->dialog != NULL && XtIsRealized( seq->dialog ) )
2424      ISQ_place_dialog( seq ) ;
2425 
2426    EXRETURN ;
2427 }
2428 
2429 /*-----------------------------------------------------------------------
2430    copy an imseq status structure
2431 -------------------------------------------------------------------------*/
2432 
ISQ_copy_status(MCW_imseq_status * instat)2433 MCW_imseq_status * ISQ_copy_status( MCW_imseq_status * instat )
2434 {
2435    MCW_imseq_status * outstat ;
2436 
2437 ENTRY("ISQ_copy_status") ;
2438 
2439    outstat = (MCW_imseq_status *) XtMalloc( sizeof(MCW_imseq_status) ) ;
2440 
2441    *outstat = *instat ;   /* shallow copy for now (no pointers) */
2442    RETURN(outstat) ;
2443 }
2444 
2445 /*-----------------------------------------------------------------------*/
2446 /* Labels for the opacity arrow at the right of the viewer */
2447 
ISQ_opacity_label(int val)2448 char * ISQ_opacity_label( int val ) /* 07 Mar 2001 */
2449 {
2450    static char dig[] = "0123456789" , buf[3] ;
2451 
2452    buf[0] = dig[val] ; buf[1] = '\0' ; return buf ;
2453 }
2454 
2455 /*-----------------------------------------------------------------------*/
2456 /* Return the (i,j,k) of the current image crosshairs,
2457    retrieved from AFNI (if available)
2458 *//*---------------------------------------------------------------------*/
2459 
ISQ_get_crosshairs(MCW_imseq * seq)2460 int_triple ISQ_get_crosshairs( MCW_imseq *seq ) /* 27 Aug 2009 */
2461 {
2462    int_triple xyn ; ISQ_cbs cbs ;
2463 
2464    cbs.reason = isqCR_getxynim ;
2465    cbs.xim = cbs.yim = cbs.nim = -666 ;  /* initialize to badness */
2466    if( seq->status->send_CB != NULL ) SEND(seq,cbs) ;
2467    xyn.i = cbs.xim ; xyn.j = cbs.yim ; xyn.k = cbs.nim ;
2468    return xyn ;
2469 }
2470 
2471 /*-----------------------------------------------------------------------*/
2472 /* What happens when the opacity arrow is clicked up or down */
2473 
ISQ_opacity_CB(MCW_arrowval * av,XtPointer cd)2474 void ISQ_opacity_CB( MCW_arrowval *av , XtPointer cd ) /* 07 Mar 2001 */
2475 {
2476    MCW_imseq *seq = (MCW_imseq *) cd ;
2477    char *buf = ISQ_opacity_label(av->ival) ;
2478    XmString xstr = XmStringCreateLtoR( buf , XmFONTLIST_DEFAULT_TAG ) ;
2479 
2480    XtVaSetValues( av->wlabel , XmNlabelString , xstr , NULL ) ;
2481    XmStringFree( xstr ) ;
2482 
2483    seq->ov_opacity = OPACITY_FAC * av->ival ;
2484    ISQ_redisplay( seq , -1 , isqDR_display ) ;
2485 
2486    if( AFNI_yesenv("AFNI_OPACITY_LOCK") )  /* 06 Jun 2019 */
2487      OPACITY_CHANGE(seq,av->ival) ;
2488 
2489    return ;
2490 }
2491 
2492 /*-----------------------------------------------------------------------*/
2493 /*! Callback for the zoom arrowval buttons */
2494 
ISQ_zoom_av_CB(MCW_arrowval * apv,XtPointer cd)2495 void ISQ_zoom_av_CB( MCW_arrowval *apv , XtPointer cd ) /* 11 Mar 2002 */
2496 {
2497    MCW_imseq    *seq = (MCW_imseq *) cd ;
2498    MCW_arrowval *av  = seq->zoom_val_av ;
2499    XmString xstr ;
2500    int zlev=av->ival ,       /* new zoom level */
2501        zold=seq->zoom_fac ;  /* old zoom level */
2502 
2503 ENTRY("ISQ_zoom_av_CB") ;
2504 
2505    if( !ISQ_REALZ(seq) || av != apv ) EXRETURN ;  /* bad */
2506 
2507    /* don't allow zoom > 1 if montage-izing */
2508 
2509    if( seq->mont_nx > 1 || seq->mont_ny > 1 ){   /* 18 Nov 2003 */
2510      AV_assign_ival(av,ZOOM_BOT) ; seq->zoom_fac = 1 ;
2511      XBell(seq->dc->display,100); EXRETURN;
2512    }
2513    if( seq->dialog != NULL && seq->dialog_starter == NBUT_MONT ){
2514      AV_assign_ival(av,ZOOM_BOT) ; seq->zoom_fac = 1 ;
2515      XBell(seq->dc->display,100); EXRETURN;
2516    }
2517 
2518    /*-- change zoom factor (and label) --*/
2519 
2520    xstr = XmStringCreateLtoR( (zlev==1)?"z":"Z" , XmFONTLIST_DEFAULT_TAG );
2521    XtVaSetValues( av->wlabel , XmNlabelString , xstr , NULL ) ;
2522    XmStringFree( xstr ) ;
2523 
2524    seq->zoom_fac = zlev ;   /* change recorded zoom factor */
2525    if( zlev == 1 ){
2526       seq->zoom_hor_off = seq->zoom_ver_off = 0.0 ; /* no image offsets */
2527    } else {
2528       float mh = (zlev-1.001f)/zlev ;          /* max offset allowed */
2529       float dh = 0.5f*(1.0f/zold-1.0f/zlev) ;  /* change in offset to */
2530                                                /* keep current center */
2531       seq->zoom_hor_off += dh ;
2532       seq->zoom_ver_off += dh ;
2533            if( seq->zoom_hor_off > mh  ){ seq->zoom_hor_off = mh  ; }
2534       else if( seq->zoom_hor_off < 0.0 ){ seq->zoom_hor_off = 0.0 ; }
2535            if( seq->zoom_ver_off > mh  ){ seq->zoom_ver_off = mh  ; }
2536       else if( seq->zoom_ver_off < 0.0 ){ seq->zoom_ver_off = 0.0 ; }
2537    }
2538 
2539    /* change some widgets depending on zoom level */
2540 
2541    SENSITIZE( seq->zoom_drag_pb , (zlev>1) ) ;
2542 
2543    AV_SENSITIZE_DOWN( av , (zlev > 1       ) ) ;
2544    AV_SENSITIZE_UP  ( av , (zlev < ZOOM_TOP) ) ;
2545 
2546    if( zlev == 1 && seq->zoom_button1 ){       /* can't pan at zlev=1 */
2547       seq->zoom_button1 = 0 ;
2548       MCW_invert_widget( seq->zoom_drag_pb ) ;
2549       POPUP_cursorize( seq->wimage ) ;
2550    }
2551 
2552    /* free the zooming pixmap (need a new one for a new image size) */
2553 
2554    if( seq->zoom_pixmap != (Pixmap) 0 ){
2555       XFreePixmap( seq->dc->display , seq->zoom_pixmap ) ;
2556       seq->zoom_pixmap = (Pixmap) 0 ; seq->zoom_pw = seq->zoom_ph = 0 ;
2557    }
2558 
2559    /* free zoomed image (need a new one for a new image size) */
2560 
2561    MCW_kill_XImage(seq->zoom_xim) ; seq->zoom_xim = NULL ;
2562 
2563    /* must redisplay image totally */
2564 
2565    ISQ_redisplay( seq , -1 , isqDR_display ) ;
2566    ZOOM_CHANGE(seq) ; /* 10 Dec 2019 */
2567 
2568    EXRETURN ;
2569 }
2570 
2571 /*--------------------------------------------------------------------------*/
2572 /*! Callback for 'pan' button - used when zoom is on, of course  */
2573 
ISQ_zoom_pb_CB(Widget w,XtPointer client_data,XtPointer call_data)2574 void ISQ_zoom_pb_CB( Widget w , XtPointer client_data , XtPointer call_data )
2575 {
2576    MCW_imseq *seq = (MCW_imseq *) client_data ;
2577 
2578 ENTRY("ISQ_zoom_pb_CB") ;
2579 
2580    if( ! ISQ_REALZ(seq)       ||
2581        w != seq->zoom_drag_pb ||
2582        seq->zoom_fac == 1       ) EXRETURN ; /* bad */
2583 
2584    if( seq->cursor_state != CURSOR_NORMAL ){ /* really bad */
2585      XBell(XtDisplay(w),100); EXRETURN;
2586    }
2587 
2588    seq->zoom_button1 = !seq->zoom_button1 ; /* toggle dragging */
2589 
2590    /* change the cursor */
2591    if( seq->zoom_button1 ) HAND_cursorize ( seq->wimage ) ;
2592    else                    POPUP_cursorize( seq->wimage ) ;
2593 
2594    MCW_invert_widget( seq->zoom_drag_pb ) ;
2595 
2596    if( seq->crop_drag ){                       /* turn off crop */
2597      MCW_invert_widget( seq->crop_drag_pb ) ;  /* button, if on */
2598      seq->crop_drag = 0 ;
2599    }
2600 
2601    EXRETURN ;
2602 }
2603 
2604 /*-------------------------------------------------------------------------*/
2605 /*! Actually pan the zoomed image, lr steps left/right, ud steps up/down.
2606 ---------------------------------------------------------------------------*/
2607 
ISQ_actually_pan(MCW_imseq * seq,int lr,int ud)2608 void ISQ_actually_pan( MCW_imseq *seq , int lr , int ud )  /* 24 Jan 2003 */
2609 {
2610    float hh,vv , mh,dh , hhold,vvold ;
2611 
2612 ENTRY("ISQ_actually_pan") ;
2613 
2614    if( !ISQ_REALZ(seq) || seq->zoom_fac == 1 || seq->zoom_xim == NULL ) EXRETURN;
2615 
2616    mh = (seq->zoom_fac-1.001f)/seq->zoom_fac ; /* max offset    */
2617    dh = 0.020/seq->zoom_fac ;                  /* delta offset   */
2618    hh=seq->zoom_hor_off ; hhold=hh ;           /* current offsets */
2619    vv=seq->zoom_ver_off ; vvold=vv ;
2620 
2621    hh += lr*dh ;
2622         if( hh < 0.0) hh = 0.0 ;
2623    else if( hh > mh ) hh = mh  ;
2624 
2625    vv += ud*dh ;
2626         if( vv < 0.0) vv = 0.0 ;
2627    else if( vv > mh ) vv = mh  ;
2628 
2629    if( vv == vvold && hh == hhold ) EXRETURN ; /* no changes? */
2630 
2631    seq->zoom_hor_off = hh ;                    /* changes! */
2632    seq->zoom_ver_off = vv ;
2633    ISQ_show_zoom( seq ) ;                      /* redraw */
2634    ZOOM_CHANGE(seq) ;      /* 10 Dec 2019 */
2635    EXRETURN ;
2636 }
2637 
2638 /*--------------------------------------------------------------------------*/
2639 /*! Callback for 'crop' button. */
2640 
ISQ_crop_pb_CB(Widget w,XtPointer client_data,XtPointer call_data)2641 void ISQ_crop_pb_CB( Widget w , XtPointer client_data , XtPointer call_data )
2642 {
2643    MCW_imseq *seq = (MCW_imseq *) client_data ;
2644 
2645 ENTRY("ISQ_crop_pb_CB") ;
2646 
2647    if( !ISQ_REALZ(seq)        ||
2648        w != seq->crop_drag_pb ||
2649        ! seq->crop_allowed      ){ /* XBell(XtDisplay(w),100); */ EXRETURN; }
2650 
2651    MCW_invert_widget( seq->crop_drag_pb ) ;
2652    seq->crop_drag = !seq->crop_drag ;
2653 
2654    if( !seq->crop_drag && seq->cropit ){                      /* turn crop off */
2655      seq->cropit = 0; seq->crop_nxorg = seq->crop_nyorg = -1; /* if double-pressed */
2656      ISQ_redisplay( seq , -1 , isqDR_display ) ;
2657    }
2658 
2659    if( seq->zoom_button1 ){                     /* turn pan off if on */
2660      POPUP_cursorize( seq->wimage ) ;
2661      MCW_invert_widget( seq->zoom_drag_pb ) ;
2662      seq->zoom_button1 = 0 ;
2663    }
2664 
2665    EXRETURN ;
2666 }
2667 
2668 /*-----------------------------------------------------------------------*/
2669 
ISQ_adjust_crop(MCW_imseq * seq,int dxa,int dxb,int dya,int dyb,int doit)2670 void ISQ_adjust_crop( MCW_imseq *seq ,
2671                       int dxa , int dxb , int dya , int dyb , int doit )
2672 {
2673    int new_xa, new_xb, new_ya, new_yb , ii,jj ;
2674 
2675 ENTRY("ISQ_adjust_crop") ;
2676 
2677    if( !ISQ_REALZ(seq) || seq->cropit == 0 ) EXRETURN ;
2678 
2679    if( dxa==0 && dxb==0 && dya==0 && dyb==0 ){  /* recenter */
2680      int_triple xyn ; int xcen,ycen , xwid,ywid ;
2681 
2682      /* find current location of crosshairs (if any) */
2683 
2684      xyn = ISQ_get_crosshairs( seq ) ; xcen = xyn.i ; ycen = xyn.j ;
2685      if( xcen < 0 || ycen < 0 ) EXRETURN ; /* check for bad return */
2686 
2687      xwid = seq->crop_xb - seq->crop_xa ;  /* crop region sizes */
2688      ywid = seq->crop_yb - seq->crop_ya ;
2689 
2690      /* compute new left-upper corner */
2691 
2692      new_xa = xcen - xwid/2 ; if( new_xa < 0 ) new_xa = 0 ;
2693      new_ya = ycen - ywid/2 ; if( new_ya < 0 ) new_ya = 0 ;
2694 
2695      /* compute new right-lower corner */
2696 
2697      new_xb = new_xa + xwid ;
2698      if( new_xb >= seq->crop_nxorg ){
2699        new_xb = seq->crop_nxorg - 1 ; new_xa = new_xb - xwid ;
2700      }
2701 
2702      new_yb = new_ya + ywid ;
2703      if( new_yb >= seq->crop_nyorg ){
2704        new_yb = seq->crop_nyorg - 1 ; new_ya = new_yb - ywid ;
2705      }
2706 
2707    } else {  /* change one or more crop window coordinate manually */
2708 
2709      /* get current corner coords */
2710 
2711      new_xa = seq->crop_xa ; new_xb = seq->crop_xb ;
2712      new_ya = seq->crop_ya ; new_yb = seq->crop_yb ;
2713 
2714      /* 27 Aug 2009: allow for flipped image,
2715                      to give the user a uniform experience */
2716 
2717      ISQ_unflipxy( seq , &new_xa , &new_ya ) ; /* flip to screen coords */
2718      ISQ_unflipxy( seq , &new_xb , &new_yb ) ;
2719 
2720      /* make sure (xa,ya) is before (xb,yb) */
2721 
2722      ii = MIN(new_xa,new_xb); jj = MAX(new_xa,new_xb); new_xa = ii; new_xb = jj;
2723      ii = MIN(new_ya,new_yb); jj = MAX(new_ya,new_yb); new_ya = ii; new_yb = jj;
2724 
2725      new_xa += dxa ; new_xb += dxb ;  /* NOW adjust crop window coords */
2726      new_ya += dya ; new_yb += dyb ;  /*   in screen coords, not image */
2727 
2728      ISQ_flipxy( seq , &new_xa , &new_ya ) ; /* flip back to image coords */
2729      ISQ_flipxy( seq , &new_xb , &new_yb ) ;
2730 
2731      /* make sure (xa,ya) is before (xb,yb) */
2732 
2733      ii = MIN(new_xa,new_xb); jj = MAX(new_xa,new_xb); new_xa = ii; new_xb = jj;
2734      ii = MIN(new_ya,new_yb); jj = MAX(new_ya,new_yb); new_ya = ii; new_yb = jj;
2735 
2736    } /* new_?? variables now contain image coords of new crop region */
2737 
2738    /* check for bad-ositiness */
2739 
2740    if( new_xa < 0 || new_ya < 0  ) EXRETURN ;  /* all these are bad */
2741    if( new_xa+MINCROP >= new_xb  ) EXRETURN ;
2742    if( new_ya+MINCROP >= new_yb  ) EXRETURN ;
2743    if( new_xb >= seq->crop_nxorg ) EXRETURN ;
2744    if( new_yb >= seq->crop_nyorg ) EXRETURN ;
2745 
2746    /* now apply the new crop window coordinates */
2747 
2748    seq->crop_xa = new_xa ; seq->crop_xb = new_xb ;
2749    seq->crop_ya = new_ya ; seq->crop_yb = new_yb ;
2750    if( doit ) ISQ_redisplay( seq , -1 , isqDR_display ) ;
2751    EXRETURN ;
2752 }
2753 
2754 /*---------------------------------------------------------------------*/
2755 
ISQ_set_crop_hint(MCW_imseq * seq)2756 void ISQ_set_crop_hint( MCW_imseq *seq )
2757 {
2758    if( !ISQ_REALZ(seq) ) return ;
2759 
2760    if( !seq->cropit ){
2761      MCW_register_hint( seq->crop_drag_pb , "Crop image" ) ;
2762    } else {
2763      static char str[256] ;
2764      sprintf(str,"Crop image: %d..%d[w=%d] X %d..%d[h=%d]" ,
2765              seq->crop_xa , seq->crop_xb , seq->crop_xb-seq->crop_xa+1 ,
2766              seq->crop_ya , seq->crop_yb , seq->crop_yb-seq->crop_ya+1  ) ;
2767      MCW_register_hint( seq->crop_drag_pb , str ) ;
2768    }
2769 
2770    return ;
2771 }
2772 
2773 /*---------------------------------------------------------------------
2774    Handle the user's action on the Button 3 popup on the crop button
2775 -----------------------------------------------------------------------*/
2776 
ISQ_butcrop_choice_CB(Widget w,XtPointer client_data,MCW_choose_cbs * cbs)2777 void ISQ_butcrop_choice_CB( Widget w , XtPointer client_data ,
2778                                        MCW_choose_cbs *cbs   )
2779 {
2780    MCW_imseq *seq = (MCW_imseq *)client_data ;
2781    float *vec = (float *)(cbs->cval) ;
2782    int ww , hh , new_xa , new_xb , new_ya , new_yb , oww,ohh ;
2783 
2784    if( !ISQ_REALZ(seq) || vec == NULL ) return ;
2785 
2786    ww = (int)vec[0] ; hh = (int)vec[1] ;
2787 
2788    if( seq->cropit && seq->crop_nxorg > 0 ){
2789      oww = seq->crop_nxorg ; ohh = seq->crop_nyorg ;
2790    } else {
2791      oww = seq->horig ; ohh = seq->vorig ;
2792    }
2793 
2794    if( ww < MINCROP || hh < MINCROP ) return ;
2795    if( ww >= oww    || hh >= ohh    ) return ;
2796 
2797    new_xa = (oww - ww) / 2 ; new_xb = new_xa + ww-1 ;
2798    new_ya = (ohh - hh) / 2 ; new_yb = new_ya + hh-1 ;
2799 
2800    if( new_xa         <  0      ) return ;
2801    if( new_ya         <  0      ) return ;
2802    if( new_xa+MINCROP >= new_xb ) return ;
2803    if( new_ya+MINCROP >= new_yb ) return ;
2804    if( new_xb         >= oww    ) return ;
2805    if( new_yb         >= ohh    ) return ;
2806 
2807    seq->crop_xa = new_xa ; seq->crop_xb = new_xb ;
2808    seq->crop_ya = new_ya ; seq->crop_yb = new_yb ;
2809    seq->cropit  = 1 ;
2810    ISQ_redisplay( seq , -1 , isqDR_display ) ;
2811    return ;
2812 }
2813 
2814 /*--------------------------------------------------------------------
2815   Button 3 action for Disp button  [17 Jun 2011]
2816 ----------------------------------------------------------------------*/
2817 
ISQ_butdisp_EV(Widget w,XtPointer client_data,XEvent * ev,RwcBoolean * continue_to_dispatch)2818 void ISQ_butdisp_EV( Widget w , XtPointer client_data ,
2819                      XEvent *ev , RwcBoolean *continue_to_dispatch )
2820 {
2821    MCW_imseq *seq = (MCW_imseq *)client_data ;
2822 
2823    if( !ISQ_REALZ(seq) ) return ;
2824    ISQ_timer_stop(seq) ;
2825 
2826    switch( ev->type ){
2827      default: break ;
2828      case ButtonPress:{
2829        XButtonEvent *event = (XButtonEvent *)ev ;
2830        if( event->button == Button3 && seq->status->send_CB != NULL ){
2831          ISQ_cbs cbs ;
2832          cbs.reason = isqCR_raiseupthedead ; SEND(seq,cbs) ;
2833        } else if( event->button == Button2 ){
2834          XBell(XtDisplay(w),100) ;
2835          MCW_popup_message( w, " \n Don't! \n "        , MCW_USER_KILL|MCW_QUICK_KILL );
2836        } else if( event->button == Button4 ){
2837          MCW_popup_message( w, " \n That tickles! \n " , MCW_USER_KILL|MCW_QUICK_KILL ) ;
2838        } else if( event->button == Button5 ){
2839          MCW_popup_message( w, " \n Please stop \n "   , MCW_USER_KILL|MCW_QUICK_KILL ) ;
2840        }
2841      }
2842      break ;
2843    }
2844    return ;
2845 }
2846 
2847 /*--------------------------------------------------------------------
2848   make Button 3 popup for Crop button
2849 ----------------------------------------------------------------------*/
2850 
ISQ_butcrop_EV(Widget w,XtPointer client_data,XEvent * ev,RwcBoolean * continue_to_dispatch)2851 void ISQ_butcrop_EV( Widget w , XtPointer client_data ,
2852                      XEvent *ev , RwcBoolean *continue_to_dispatch )
2853 {
2854    MCW_imseq *seq = (MCW_imseq *)client_data ;
2855 
2856    if( !ISQ_REALZ(seq) ) return ;
2857    ISQ_timer_stop(seq) ;
2858 
2859    switch( ev->type ){
2860      default: break ;
2861       case ButtonPress:{
2862          XButtonEvent *event = (XButtonEvent *) ev ;
2863          if( event->button == Button3 ){
2864            char *lvec[2] = { "Width " , "Height" } ;
2865            float fvec[2] ; int oww=0,ohh=0 ;
2866            if( seq->cropit ){
2867              oww = seq->crop_xb -seq->crop_xa + 1 ;
2868              ohh = seq->crop_yb -seq->crop_ya + 1 ;
2869            }
2870            if( oww < MINCROP ) oww = seq->horig / 2 ;
2871            if( ohh < MINCROP ) ohh = seq->vorig / 2 ;
2872            if( oww < MINCROP ) oww = MINCROP ;
2873            if( ohh < MINCROP ) ohh = MINCROP ;
2874            fvec[0] = oww ; fvec[1] = ohh ;
2875            if( oww >= MINCROP && ohh >= MINCROP ){
2876              MCW_choose_vector(
2877                 seq->crop_drag_pb ,
2878                 "--------------------------------------------\n"
2879                 "Choose width and height of image crop window\n"
2880                 "     (minimum allowed size is 9 pixels)\n"
2881                 "   Crop window will be centered on image:\n"
2882                 "    Adjust with Shift+Keypad_Arrow_Keys.\n"
2883                 "--------------------------------------------"  ,
2884                 2 , lvec , fvec , ISQ_butcrop_choice_CB , (XtPointer)seq ) ;
2885            }
2886 
2887          } else if( event->button == Button2 ){
2888             XBell(XtDisplay(w),100) ;
2889             MCW_popup_message( w,
2890                                lrand48()%2 == 0 ? " \n Ooch! \n "
2891                                                 : "Don't\n DO\nthat!" ,
2892                                MCW_USER_KILL|MCW_QUICK_KILL );
2893             /** AFNI_speak( "Ouch!" , 0 ) ; **/
2894          }
2895       }
2896       break ;
2897    }
2898    return ;
2899 }
2900 
2901 /*-----------------------------------------------------------------------*/
2902 /* Convert a short image of indexes into color tables to RGB.
2903    overlay != 0 ==> use overlay color table only
2904                     otherwise, use underlay table for positive values
2905                            and use overlay table for netative values
2906 *//*---------------------------------------------------------------------*/
2907 
ISQ_index_to_rgb(MCW_DC * dc,int overlay,MRI_IMAGE * im)2908 MRI_IMAGE * ISQ_index_to_rgb( MCW_DC *dc , int overlay , MRI_IMAGE *im ) /* 07 Mar 2001 */
2909 {
2910    register int npix,ii,jj ;
2911    MRI_IMAGE *outim ;
2912    register byte *our ;
2913    register short *iar ;
2914 
2915 ENTRY("ISQ_short_to_rgb") ;
2916 
2917    if( dc == NULL || im == NULL || im->kind != MRI_short ) RETURN(NULL) ;
2918 
2919    npix  = im->nvox ;
2920    iar   = MRI_SHORT_PTR(im) ;
2921    outim = mri_new_conforming( im , MRI_rgb ) ;
2922    our   = MRI_RGB_PTR(outim) ;
2923 
2924    if( !overlay ){
2925       for( jj=ii=0 ; ii < npix ; ii++,jj+=3 ){
2926          if( iar[ii] >= 0 ){                         /* pos => underlay table */
2927             our[jj  ] = DC_REDBYTE  (dc,iar[ii]) ;
2928             our[jj+1] = DC_GREENBYTE(dc,iar[ii]) ;
2929             our[jj+2] = DC_BLUEBYTE (dc,iar[ii]) ;
2930          } else {                                    /* neg => overlay table */
2931             our[jj  ] = DCOV_REDBYTE  (dc,-iar[ii]) ;
2932             our[jj+1] = DCOV_GREENBYTE(dc,-iar[ii]) ;
2933             our[jj+2] = DCOV_BLUEBYTE (dc,-iar[ii]) ;
2934          }
2935       }
2936    } else {                                      /* use overlay table only */
2937       for( jj=ii=0 ; ii < npix ; ii++,jj+=3 ){
2938          if( iar[ii] > 0 ){                         /* valid overlay index */
2939             our[jj  ] = DCOV_REDBYTE(dc,iar[ii]) ;
2940             our[jj+1] = DCOV_GREENBYTE(dc,iar[ii]) ;
2941             our[jj+2] = DCOV_BLUEBYTE(dc,iar[ii]) ;
2942          } else {                                   /* not valid */
2943             our[jj] = our[jj+1] = our[jj+2] = 0 ;
2944          }
2945       }
2946    }
2947 
2948    RETURN(outim) ;
2949 }
2950 
2951 /*-----------------------------------------------------------------------
2952    Overlay one image onto another.  Underlay (ulim) and overlay (ovim)
2953    must be either shorts or rgb.  If they are shorts, they are indices
2954    into the underlay and overlay color index tables, respectively.
2955    * Usually the underlay table is grayscale, and the overlay table colors,
2956      but the underlay table can be colors if the user turns 'Colr' on.
2957    The overlay opacity is alpha (0 < alpha <= 1).
2958      If both are shorts and alpha=1, the output is shorts.
2959      If either is rgb, or alpha < 1, then the output is rgb.
2960      If ulim is rgba, then output is rgba [13 Feb 2020].
2961      The output is NULL if the inputs are invalid.
2962    Pixels from ovim are overlaid only if they are NOT zero.
2963    -- 06 Mar 2001 -- RWCox
2964 -------------------------------------------------------------------------*/
2965 
ISQ_overlay(MCW_DC * dc,MRI_IMAGE * ulim,MRI_IMAGE * ovim,float alpha)2966 MRI_IMAGE * ISQ_overlay( MCW_DC *dc , MRI_IMAGE *ulim, MRI_IMAGE *ovim, float alpha )
2967 {
2968    register int npix,ii,jj ;
2969    MRI_IMAGE *outim=NULL , *orim ;
2970    register byte *orr, *our ;
2971 
2972 ENTRY("ISQ_overlay") ;
2973 
2974    if( dc == NULL || ulim == NULL || ovim == NULL || alpha <= 0.0 ) RETURN(NULL) ;
2975 
2976    npix = ulim->nvox ;
2977 
2978    if( ovim->nvox != npix ) RETURN(NULL) ;  /* this is bad - user = loser */
2979 
2980    if( alpha > 1.0f ) alpha = 1.0f ;       /* stoopid user loser */
2981 
2982    /*-- Case: both are short indices, no transparency (alpha == 1) --*/
2983    /*--       output image will be shorts as well                  --*/
2984 
2985    if( ulim->kind == MRI_short && ovim->kind == MRI_short && alpha > 0.99f ){
2986      register short *tar , *oar=MRI_SHORT_PTR(ovim) , *iar=MRI_SHORT_PTR(ulim) ;
2987 
2988      outim = mri_new_conforming( ulim , MRI_short ) ;
2989      tar   = MRI_SHORT_PTR( outim ) ;
2990      for( ii=0 ; ii < npix ; ii++ )
2991        tar[ii] = (oar[ii] <= 0) ? iar[ii] : -oar[ii] ;
2992 
2993      RETURN(outim) ;
2994    }
2995 
2996    /*-- Case: underlay input is RGBA [13 Feb 2020] --*/
2997    /*--       output image will be RGBA as well    --*/
2998 
2999    if( ulim->kind == MRI_rgba ){
3000      outim = ISQ_overlay_rgba( dc , ulim , ovim , alpha ) ;
3001      RETURN(outim) ;
3002    }
3003 
3004    /*-- Convert underlay to RGB, if needed  --*/
3005    /*-- Output image will be RGB below here --*/
3006 
3007    switch( ulim->kind ){              /* we always make a new RGB underlay,  */
3008      case MRI_rgb:                    /* since this will be the output image */
3009        outim = mri_copy(ulim) ;
3010        our   = MRI_RGB_PTR(outim) ;
3011      break ;
3012 
3013 #if 0  /* should no longer be possible */
3014      case MRI_rgba:
3015        outim = mri_to_rgb(ulim) ;
3016        our   = MRI_RGB_PTR(outim) ;
3017      break ;
3018 #endif
3019 
3020      default:
3021        RETURN(NULL) ; break ;   /* bad bad bad - user should be flogged */
3022 
3023      case MRI_short:
3024        outim = ISQ_index_to_rgb( dc , 0 , ulim ) ; /* grayscale from indexes */
3025        our   = MRI_RGB_PTR(outim) ;
3026      break ;
3027    }
3028 
3029    /*-- overlay conversion --*/
3030 
3031    switch( ovim->kind ){    /* but we don't make a new overlay unless needed */
3032      case MRI_rgb:
3033        orim = ovim ; orr = MRI_RGB_PTR(orim) ; break ;
3034 
3035      default:
3036        mri_free(outim) ;
3037        RETURN(NULL) ; break ;              /* bad bad bad */
3038 
3039      case MRI_short:
3040        orim = ISQ_index_to_rgb( dc , 1 , ovim ) ;  /* colors from indexes */
3041        orr  = MRI_RGB_PTR(orim) ;
3042      break ;
3043 
3044      case MRI_rgba:{                                         /* 08 Dec 2014 */
3045        rgba *ovar = MRI_RGBA_PTR(ovim) ; byte rr,gg,bb,aa ;  /* RGBA special case */
3046        register float al=alpha , am,bm ;
3047        for( jj=ii=0 ; ii < npix ; ii++,jj+=3 ){
3048           rr = ovar[ii].r; gg = ovar[ii].g; bb = ovar[ii].b; aa = ovar[ii].a;
3049           if( aa > 0 && (rr > 0 || gg > 0 || bb > 0 ) ){
3050             am = aa*al/255.0f ; bm = 1.0f-am ;  /* pixelwise mixing fracs */
3051             our[jj  ] = am*rr + bm*our[jj  ] ;  /* mix colors betwixt ovar */
3052             our[jj+1] = am*gg + bm*our[jj+1] ;  /* and our; overwrite our */
3053             our[jj+2] = am*bb + bm*our[jj+2] ;
3054            }
3055        }
3056        RETURN(outim) ;
3057      }
3058      break ;
3059    }
3060 
3061    /* now overlay (same mixing frac per pixel) */
3062 
3063    if( alpha > 0.99f ){                          /* opaque overlay */
3064       for( jj=ii=0 ; ii < npix ; ii++,jj+=3 ){
3065          if( orr[jj] > 0 || orr[jj+1] > 0 || orr[jj+2] > 0 ){
3066             our[jj  ] = orr[jj  ] ;
3067             our[jj+1] = orr[jj+1] ;
3068             our[jj+2] = orr[jj+2] ;
3069          }
3070       }
3071    } else if (!my_getenv("AFNI_MIX_BY_BRIGHT")) {     /* translucent overlay */
3072       register float aa=alpha , bb=1.0-alpha ;
3073       for( jj=ii=0 ; ii < npix ; ii++,jj+=3 ){
3074          if( orr[jj] > 0 || orr[jj+1] > 0 || orr[jj+2] > 0 ){
3075             our[jj  ] = aa*orr[jj  ] + bb*our[jj  ] ;  /* mix colors */
3076             our[jj+1] = aa*orr[jj+1] + bb*our[jj+1] ;
3077             our[jj+2] = aa*orr[jj+2] + bb*our[jj+2] ;
3078          }
3079       }
3080    } else {      /* mix by scaling color with brightness of background - ZSS */
3081       register float aa=alpha, bb,
3082                      mings, maxgs, *gs=NULL,
3083                      MaxGain = 2.0-alpha*alpha,
3084                      MinGain = alpha*alpha ;
3085       gs = (float *)malloc(sizeof(float)*npix);
3086       /* calculate grey scale, keep track of range */
3087       mings = 255*3.0; maxgs = 0;
3088       for( jj=ii=0 ; ii < npix ; ii++,jj+=3 ){
3089          if( orr[jj] > 0 || orr[jj+1] > 0 || orr[jj+2] > 0 ){
3090             gs[ii] = (our[jj  ]+our[jj+1]+orr[jj+2]);
3091             if (gs[ii] < mings) mings = gs[ii];
3092             else if (gs[ii] > maxgs) maxgs = gs[ii];
3093          } else {
3094             gs[ii] = 0.0;
3095          }
3096       }
3097       /* now scale gs values */
3098       bb = (MaxGain - MinGain)/(maxgs-mings);
3099       for( ii=0 ; ii < npix ; ii++ ){
3100          if( gs[ii] ) gs[ii] =  bb * (gs[ii]-mings)+MinGain;
3101       }
3102       for( jj=ii=0 ; ii < npix ; ii++,jj+=3 ){
3103          if( gs[ii] ){ /* Colors will change here, not just a brightness
3104                           modulation. Need much fancier method to deal
3105                           with mixing appropriately */
3106                bb = (gs[ii])*orr[jj  ]  ;  /* mix colors */
3107                if (bb > 255) our[jj  ] = 255; else our[jj  ] = (byte)bb;
3108                bb = (gs[ii])*orr[jj+1]  ;
3109                if (bb > 255) our[jj+1] = 255; else our[jj+1] = (byte)bb;
3110                bb = (gs[ii])*orr[jj+2]  ;
3111                if (bb > 255) our[jj+2] = 255; else our[jj+2] = (byte)bb;
3112          }
3113       }
3114       free(gs); gs=NULL;
3115    }
3116 
3117    if( orim != ovim ) mri_free(orim) ;  /* destroy copy of overlay, if any */
3118 
3119    RETURN(outim) ;
3120 }
3121 
3122 /*-----------------------------------------------------------------------*/
3123 /* Always produce RGBA output [13 Feb 2020] */
3124 
ISQ_overlay_rgba(MCW_DC * dc,MRI_IMAGE * ulim,MRI_IMAGE * ovim,float alpha)3125 MRI_IMAGE * ISQ_overlay_rgba( MCW_DC *dc , MRI_IMAGE *ulim, MRI_IMAGE *ovim, float alpha )
3126 {
3127    register int npix,ii,jj ;
3128    MRI_IMAGE *outim , *ulimNEW , *ovimNEW , *qim ;
3129    rgba *outar , *ular , *ovar ;
3130    int jill = AFNI_yesenv("AFNI_JILL_TRAVESTY") ; /* Jill is trouble */
3131 
3132 ENTRY("ISQ_overlay_rgba") ;
3133 
3134    if( dc == NULL || ulim == NULL || ovim == NULL || alpha <= 0.0 ) RETURN(NULL) ;
3135 
3136    npix = ulim->nvox ;
3137 
3138    if( ovim->nvox != npix ) RETURN(NULL) ;  /* this is bad */
3139 
3140    if( alpha > 1.0f ) alpha = 1.0f ;       /* stoopid user */
3141 
3142    /*-- Convert both inputs to RGBA, if needed --*/
3143 
3144    switch( ulim->kind ){
3145      default:
3146        if( jill ) ININFO_message(" - underlay = ILLEGAL type") ;
3147        RETURN(NULL) ; break ;   /* bad bad bad */
3148 
3149      case MRI_rgba:      /* no conversion needed */
3150        ulimNEW = ulim ;
3151        if( jill ) ININFO_message(" - underlay = RGBA");
3152      break ;
3153 
3154      case MRI_rgb:
3155        ulimNEW = mri_to_rgba(ulim) ;
3156        if( jill ) ININFO_message(" - underlay = RGB to RGBA");
3157      break ;
3158 
3159      case MRI_short:
3160        qim = ISQ_index_to_rgb( dc , 1 , ulim ) ; /* colors from indexes */
3161        ulimNEW = mri_to_rgba(qim) ;              /* NOT grayscale */
3162        mri_free(qim) ;
3163        if( jill ) ININFO_message(" - underlay = SHORT to RGBA");
3164      break ;
3165    }
3166 
3167    outim = mri_copy(ulimNEW) ;     /* initial output = copy of underlay input */
3168    outar = MRI_RGBA_PTR(outim) ;
3169    ular  = MRI_RGBA_PTR(ulimNEW) ;
3170 
3171    /*-- conversion of the overlay input image --*/
3172 
3173    switch( ovim->kind ){
3174      case MRI_rgba:       /* no conversion needed */
3175        ovimNEW = ovim ;
3176        if( jill ) ININFO_message(" - overlay = RGBA");
3177      break ;
3178 
3179      case MRI_rgb:
3180        ovimNEW = mri_to_rgba(ovim) ;
3181        if( jill ) ININFO_message(" - overlay = RGB to RGBA");
3182      break ;
3183 
3184      default:                                    /* bad bad bad */
3185        mri_free(outim) ;
3186        if( ulimNEW != ulim ) mri_free(ulimNEW) ;
3187        if( jill ) ININFO_message(" - overlay = ILLEGAL type") ;
3188      RETURN(NULL) ; break ;
3189 
3190      case MRI_short:
3191        qim = ISQ_index_to_rgb( dc , 1 , ovim ) ; /* colors from indexes */
3192        ovimNEW = mri_to_rgba(qim) ;
3193        mri_free(qim) ;
3194        if( jill ) ININFO_message(" - overlay = SHORT to RGBA");
3195      break ;
3196    }
3197 
3198    ovar = MRI_RGBA_PTR(ovimNEW) ;
3199 
3200    /* merger of RGBA data -- see
3201       https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending */
3202 
3203    if( jill ) ININFO_message(" - begin alpha blending") ;
3204 
3205    { float al=alpha , oam,obm , uuu ;
3206      byte orr,ogg,obb,oaa , urr,ugg,ubb,uaa ; int n1=0,n2=0,n3=0 ;
3207      for( ii=0 ; ii < npix ; ii++ ){
3208        orr = ovar[ii].r; ogg = ovar[ii].g; obb = ovar[ii].b; oaa = ovar[ii].a;
3209        urr = ular[ii].r; ugg = ular[ii].g; ubb = ular[ii].b; uaa = ular[ii].a;
3210        if( oaa > 0 && (orr > 0 || ogg > 0 || obb > 0 ) ){
3211          oam = oaa*al/255.0f ; obm = 1.0f-oam ;  /* mixing fracs for overlay */
3212          if( oam > 0.99f ){        /* opaque overlay */
3213            outar[ii].r = orr ;
3214            outar[ii].g = ogg ;
3215            outar[ii].b = obb ;
3216            outar[ii].a = 255 ; n1++ ;
3217          } else if( uaa > 252 ){  /* opaque underlay */
3218            outar[ii].r = (byte)(oam*orr + obm*urr + 0.4f) ;
3219            outar[ii].g = (byte)(oam*ogg + obm*ugg + 0.4f) ;
3220            outar[ii].b = (byte)(oam*obb + obm*ubb + 0.4f) ;
3221            outar[ii].a = 255 ; n2++ ;
3222          } else {                  /* non-opaque underlay */
3223            obm *= (uaa/255.0f) ;   /* shrink obm */
3224            uuu = oam + obm ;
3225            oam = oam / uuu ; obm = 1.0f-oam ;
3226            outar[ii].r = (byte)(oam*orr + obm*urr + 0.4f) ;
3227            outar[ii].g = (byte)(oam*ogg + obm*ugg + 0.4f) ;
3228            outar[ii].b = (byte)(oam*obb + obm*ubb + 0.4f) ;
3229            outar[ii].a = (byte)(255.0f*uuu+0.4f) ; n3++ ;
3230          }
3231        }
3232      }
3233      if( jill ) ININFO_message(" - images merged: n1=%d n2=%d n3=%d npix=%d",n1,n2,n3,npix) ;
3234    }
3235 
3236    if( ulimNEW != ulim ) mri_free(ulimNEW) ;  /* only toss them if they */
3237    if( ovimNEW != ovim ) mri_free(ovimNEW) ;  /* aren't the actual inputs */
3238 
3239    RETURN(outim) ;
3240 }
3241 
3242 /*-----------------------------------------------------------------------*/
3243 /* Produce a 0-1 mask from an overlay-type dataset */
3244 
ISQ_binarize_overlay(MRI_IMAGE * tim)3245 MRI_IMAGE * ISQ_binarize_overlay( MRI_IMAGE *tim )  /* Mar 2013 */
3246 {
3247    MRI_IMAGE *bim ; byte *bar ;
3248    int npix ;
3249 
3250 ENTRY("ISQ_binarize_overlay") ;
3251 
3252    if( tim == NULL || !ISQ_GOOD_OVERLAY_TYPE(tim->kind) ) RETURN(NULL) ;
3253 
3254    npix = tim->nvox ;
3255    bim = mri_new_conforming(tim,MRI_byte) ; bar = MRI_BYTE_PTR(bim) ;
3256 
3257    switch( tim->kind ){
3258 
3259      default:  mri_free(bim) ; RETURN(NULL) ;  /* should be impossible */
3260 
3261      case MRI_short:{
3262        short *tar = MRI_SHORT_PTR(tim) ; int ii ;
3263        for( ii=0 ; ii < npix ; ii++ )
3264          bar[ii] = (tar[ii] > 0) ;
3265      }
3266      break ;
3267 
3268      case MRI_rgb:{
3269        byte *tar = MRI_RGB_PTR(tim) ; int ii,jj ;
3270        for( ii=jj=0 ; ii < npix ; ii++,jj+=3 )
3271          bar[ii] = ( tar[jj] > 0 || tar[jj+1] > 0 || tar[jj+2] > 0 ) ;
3272      }
3273      break ;
3274 
3275      case MRI_rgba:{                 /* Oops, forgot about this [18 Aug 2020] */
3276        rgba *tar = MRI_RGBA_PTR(tim) ; int ii ;
3277        for( ii=0 ; ii < npix ; ii++ ){
3278          bar[ii] = ( tar[ii].a > 254 ) &&
3279                    ( tar[ii].r > 0 || tar[ii].g > 0 || tar[ii].b > 0 ) ;
3280        }
3281      }
3282      break ;
3283 
3284    }
3285 
3286    RETURN(bim) ;
3287 }
3288 
3289 /*-----------------------------------------------------------------------
3290    Make a color bar "given" XImage, for display next to the image
3291    viewer. Here, the MRI_IMAGE is just the short-valued indexes
3292    into the color map, which mri_to_XImage() will convert to
3293    the actual colors for pushing out the window.
3294 -------------------------------------------------------------------------*/
3295 
ISQ_make_bar(MCW_imseq * seq)3296 void ISQ_make_bar( MCW_imseq *seq )
3297 {
3298    MRI_IMAGE *im ;
3299    int iy , ny ;
3300    short *ar ;
3301 
3302 ENTRY("ISQ_make_bar") ;
3303 
3304    if( ! ISQ_VALID(seq) ) EXRETURN ;
3305 
3306    KILL_2XIM( seq->given_xbar , seq->sized_xbar ) ;
3307 
3308    ny = seq->dc->ncol_im ;
3309    im = mri_new( 1 , ny , MRI_short ) ;
3310    ar = mri_data_pointer( im ) ;
3311    for( iy=0 ; iy < ny ; iy++ ) ar[iy] = ny-1-iy ;
3312 
3313    seq->given_xbar = mri_to_XImage( seq->dc , im ) ;
3314 
3315    KILL_1MRI( im ) ;
3316    EXRETURN ;
3317 }
3318 
3319 /*------------------------------------------------------------------------*/
3320 /* Apply a binary mask to an image iim */
3321 
ISQ_apply_mask(MRI_IMAGE * maskim,MRI_IMAGE * iim)3322 void ISQ_apply_mask( MRI_IMAGE *maskim , MRI_IMAGE *iim )
3323 {
3324    byte *mmm ; int ii , npix ;
3325 
3326    if( maskim == NULL || maskim->kind != MRI_byte || iim == NULL ) return ;
3327    npix = iim->nvox ;                   if( maskim->nvox != npix ) return ;
3328    mmm = MRI_BYTE_PTR(maskim) ;                  if( mmm == NULL ) return ;
3329 
3330    switch( iim->kind ){
3331      default: break ;
3332      case MRI_byte:{
3333        byte *ar = mri_data_pointer(iim) ;
3334        for( ii=0 ; ii < npix ; ii++ ) if( mmm[ii] == 0 ) ar[ii] = 0 ;
3335      }
3336      return ;
3337 
3338      case MRI_short:{
3339        short *ar = mri_data_pointer(iim) ;
3340        for( ii=0 ; ii < npix ; ii++ ) if( mmm[ii] == 0 ) ar[ii] = 0 ;
3341      }
3342      return ;
3343 
3344      case MRI_float:{
3345        float *ar = mri_data_pointer(iim) ;
3346        for( ii=0 ; ii < npix ; ii++ ) if( mmm[ii] == 0 ) ar[ii] = 0 ;
3347      }
3348      return ;
3349 
3350      case MRI_rgb:{
3351        byte *ar = mri_data_pointer(iim) ;
3352        for( ii=0 ; ii < npix ; ii++ ) if( mmm[ii] == 0 ) ar[3*ii] = ar[3*ii+1] = ar[3*ii+2] = 0 ;
3353      }
3354      return ;
3355 
3356      case MRI_rgba:{
3357        rgba *ar = mri_data_pointer(iim) ;
3358        for( ii=0 ; ii < npix ; ii++ ) if( mmm[ii] == 0 ) ar[ii].r = ar[ii].g = ar[ii].b = ar[ii].a = 0 ;
3359      }
3360      return ;
3361 
3362      case MRI_complex:{
3363        complex *ar = mri_data_pointer(iim) ;
3364        for( ii=0 ; ii < npix ; ii++ ) if( mmm[ii] == 0 ) ar[ii].r = ar[ii].i = 0 ;
3365      }
3366      return ;
3367    }
3368 
3369    return ; /* should never be reached */
3370 }
3371 
3372 
3373 /*------------------------------------------------------------------------
3374    make the MRI_IMAGE and the XImage, given the sequence status:
3375      - if imim is NULL, get it from the user routine and process it,
3376        if imim is not NULL, leave it alone
3377      - convert into an XImage
3378      - here is where changes to the toggled display options are processed
3379 -------------------------------------------------------------------------*/
3380 
ISQ_make_image(MCW_imseq * seq)3381 void ISQ_make_image( MCW_imseq *seq )
3382 {
3383    MRI_IMAGE *im , *ovim , *tim ;
3384    RwcBoolean reset_done = False ;
3385    float vfac = VGFAC(seq) ;
3386 
3387 ENTRY("ISQ_make_image") ;
3388 
3389    if( ! ISQ_VALID(seq) ) EXRETURN ;
3390 
3391    /*-- if doing a montage, make it in a separate function --*/
3392 
3393    if( seq->mont_nx > 1 || seq->mont_ny > 1 ){
3394      ISQ_make_montage( seq ) ;
3395      EXRETURN ;
3396    }
3397 
3398    KILL_2XIM( seq->given_xim , seq->sized_xim ) ;  /* erase the XImages */
3399 
3400    if( seq->mplot != NULL ){                            /* 19 Sep 2001 */
3401      delete_memplot( seq->mplot ) ; seq->mplot = NULL ;
3402    }
3403 
3404    /* process toggled options that affect the image that may be stored */
3405 
3406    if( seq->opt.rot         != seq->old_opt.rot         ||
3407        seq->opt.mirror      != seq->old_opt.mirror      ||
3408        seq->opt.scale_group != seq->old_opt.scale_group ||
3409        seq->opt.scale_range != seq->old_opt.scale_range ||
3410        seq->mont_nx         != seq->mont_nx_old         ||
3411        seq->mont_ny         != seq->mont_ny_old           ){
3412 
3413       KILL_1MRI( seq->imim ) ;  /* must re-get image for new processing */
3414       KILL_1MRI( seq->ovim ) ;
3415    }
3416 
3417    /*--- set the image to process ---*/
3418 
3419    im = seq->imim ;
3420 
3421    if( im == NULL ){
3422       float new_width_mm , new_height_mm ;
3423 
3424       switch( seq->render_mode ){
3425         default:
3426           tim = ISQ_getimage( seq->im_nr , seq ) ;  /* might be cropped */
3427           if( tim == NULL ) EXRETURN ;
3428           seq->last_image_type = tim->kind ;
3429           seq->set_orim = (seq->need_orim != 0) ;  /* 30 Dec 1998 */
3430           seq->imim = im = ISQ_process_mri( seq->im_nr , seq , tim , 0 ) ;
3431           KILL_1MRI(tim) ;
3432           seq->set_orim = 0 ;
3433           seq->barbot = seq->clbot ; /* 29 Jul 2001 */
3434           seq->bartop = seq->cltop ;
3435         break ;
3436 
3437         case RENDER_WIPE_LEFT:    /* WIPE stuff 22 Aug 2014 */
3438         case RENDER_WIPE_BOT:
3439         case RENDER_MIX:
3440         case RENDER_WIPE_RIGHT:
3441         case RENDER_WIPE_TOP:
3442         case RENDER_CHECK_UO:
3443         case RENDER_CHECK_OU:
3444           seq->set_orim = 0 ;
3445           seq->imim = im = ISQ_getchecked( seq->im_nr ,seq ) ;
3446           if( im == NULL ) EXRETURN ;
3447           seq->last_image_type = im->kind ;
3448           seq->barbot = seq->bartop = 0.0f ;
3449         break ;
3450       }
3451       ISQ_set_barhint(seq,NULL) ;
3452 
3453       /* fix window dimensions if image size is different from before */
3454 
3455       new_width_mm  = IM_WIDTH(im) ;
3456       new_height_mm = IM_HEIGHT(im) ;
3457 
3458       seq->horig = im->nx ;  seq->last_dx = fabs(im->dx) ;
3459       seq->vorig = im->ny ;  seq->last_dy = fabs(im->dy) ;
3460 
3461       if( FLDIF(new_width_mm ,seq->last_width_mm ) ||
3462           FLDIF(new_height_mm,seq->last_height_mm)   ){
3463 
3464          if( PRINT_TRACING ){
3465            char str[256] ;
3466            sprintf(str,"nx=%d ny=%d dx=%f dy=%f wid=%f hei=%f",
3467                   im->nx,im->ny,im->dx,im->dy,new_width_mm,new_height_mm) ;
3468            STATUS(str) ;
3469          }
3470 
3471          ISQ_reset_dimen( seq , new_width_mm , new_height_mm ) ;
3472          reset_done = True ;
3473       }
3474    }
3475 
3476    if( seq->opt.free_aspect != seq->old_opt.free_aspect && !reset_done )
3477       ISQ_reset_dimen( seq , seq->last_width_mm , seq->last_height_mm ) ;
3478 
3479    /*--- set the overlay to process ---*/
3480 
3481    if( ISQ_SKIP_OVERLAY(seq) ){
3482      KILL_1MRI( seq->ovim ) ;
3483      ovim = NULL ;
3484    } else {
3485      char *lab ;        /* 20 Sep 2001 */
3486      byte *mmm=NULL ;   /* 12 Dec 2014 */
3487 
3488      ovim = seq->ovim ;
3489      if( ovim == NULL ){
3490         tim = ISQ_getoverlay( seq->im_nr , seq ) ;
3491         if( tim != NULL && !ISQ_GOOD_OVERLAY_TYPE(tim->kind) ){
3492           fprintf(stderr,"\a\n*** Illegal overlay image kind=%d! ***\n",(int)tim->kind) ;
3493           KILL_1MRI(tim) ;
3494         }
3495         if( tim != NULL )
3496           ovim = seq->ovim =
3497             mri_flippo( ISQ_TO_MRI_ROT(seq->opt.rot) , seq->opt.mirror , tim ) ;
3498         if( tim != ovim ) KILL_1MRI(tim) ;
3499         ISQ_apply_mask( seq->last_automask , ovim ) ;
3500      }
3501 
3502      /*-- 19 Sep 2001: get an overlay plot, if there is one --*/
3503 
3504      if( MCW_val_bbox(seq->wbar_plots_bbox) != 0 ){
3505        seq->mplot = ISQ_getmemplot( seq->im_nr , seq ) ;
3506        if( seq->mplot != NULL )
3507          flip_memplot( ISQ_TO_MRI_ROT(seq->opt.rot),seq->opt.mirror, seq->mplot );
3508      }
3509 
3510      /*-- 20 Sep 2001: get a label, if there is one --*/
3511 
3512      if( seq->wbar_label_av->ival != 0 ){
3513        lab = ISQ_getlabel( seq->im_nr , seq ) ;
3514        if( lab != NULL ){
3515          MEM_plotdata *mp = ISQ_plot_label( seq , lab ) ;
3516          if( mp != NULL ){
3517            if( seq->mplot != NULL ){
3518              append_to_memplot( seq->mplot , mp ) ; delete_memplot( mp ) ;
3519            } else {
3520              seq->mplot = mp ;
3521            }
3522          }
3523          free(lab) ;
3524        }
3525      }
3526 
3527    } /* end of overlay-osity */
3528 
3529    /* set old_opt to current options */
3530 
3531    seq->old_opt = seq->opt ;
3532 
3533    seq->mont_nx_old = seq->mont_ny_old = 1 ;
3534 
3535    STATUS("making given_xim");
3536 
3537    /* overlay, if needed */
3538 
3539    if( ovim == NULL || ISQ_SKIP_OVERLAY(seq) ){          /* nothing to do */
3540       tim = im ;
3541    } else {                                                /* 06 Mar 2001 */
3542       tim = ISQ_overlay( seq->dc, im, ovim, seq->ov_opacity ) ;
3543       if( tim == NULL ) tim = im ;                    /* shouldn't happen */
3544    }
3545 
3546    if( vfac > 0.0f ){ /* the Van Gogh effect - just for fun, not for science! */
3547      MRI_IMAGE *qim ;
3548      MCW_invert_widget(seq->wbut_bot[NBUT_DISP]) ;
3549      vgize_sigfac = vfac ; qim = mri_vgize(tim) ;
3550      MCW_invert_widget(seq->wbut_bot[NBUT_DISP]) ;
3551      if( qim != NULL ){
3552        if( tim != im ) KILL_1MRI(tim);
3553        tim = qim;
3554      }
3555    }
3556 
3557    /* convert result to XImage for display */
3558 
3559    STATUS("converting to XImage") ;
3560    seq->given_xim = mri_to_XImage( seq->dc , tim ) ;
3561 
3562    if( tim != im ) KILL_1MRI(tim) ;
3563 
3564    EXRETURN ;
3565 }
3566 
3567 /*-----------------------------------------------------------------------
3568   Plot a label into a structure for later display
3569 -------------------------------------------------------------------------*/
3570 
ISQ_plot_label(MCW_imseq * seq,char * lab)3571 MEM_plotdata * ISQ_plot_label( MCW_imseq *seq , char *lab )
3572 {
3573    MEM_plotdata *mp ; int ww , nlin ; float asp , dd ;
3574    static int   sz[6] = { 12     , 20    , 28    , 40    , 56    , 80     } ;
3575    static float th[6] = { 0.001f , 0.002f, 0.003f, 0.004f, 0.005f, 0.007f } ;
3576    char *eee ; float rr=1.0f,gg=1.0f,bb=0.7f , sb=0.003f , thk ;
3577 
3578 ENTRY("ISQ_plot_label") ;
3579 
3580    if( !ISQ_REALZ(seq) || lab == NULL || lab[0] == '\0' ) RETURN(NULL) ;
3581 
3582    asp = 1.0f ;  /* aspect ratio of plot */
3583 
3584    /** In the following, all coordinates are in the 'units'
3585        where the plot window runs over (x,y) = [0..1,0..asp] */
3586 
3587    /* set character size (units = 0.001 of plot width) */
3588 
3589    ww = sz[seq->wbar_labsz_av->ival] ;
3590    if( asp > 1.0f ) ww = (int)(ww/asp+0.5f) ;
3591 
3592    thk = th[seq->wbar_labsz_av->ival] ;  /* line thickness */
3593 
3594    /* find number of lines in label [15 Jul 2021] */
3595 
3596    nlin = 1 ;
3597    for( eee=lab ; *eee != '\0' ; eee++ ){
3598      if( *eee == '\n' || strncmp(eee,"\\newline",8) == 0 ) nlin++ ;
3599    }
3600 
3601    /* get the setback from edge in x direction [default = 0.003 * plot width] */
3602 
3603    eee = getenv("AFNI_IMAGE_LABEL_SETBACK") ;
3604    if( eee != NULL ){
3605       float ss = strtod(eee,NULL) ;
3606       if( ss >= 0.0 && ss < 0.5 ) sb = ss ;
3607    }
3608 
3609    /** AFNI_REPLACE_XDRAWLINES ; **/
3610 
3611    /* If string is 'long':
3612        create a temporary memplot to draw string into to get it bounding box;
3613        then use that box to change the drawing scale if the box is too wide. */
3614 
3615 #define TSIZ 4  /* smallest font size pwritf will take is 4 */
3616    if( strlen(lab) > 9 ){                    /* 19 Aug 2021 */
3617      float_quad bbox ; float xsiz,ysiz,test ;
3618      create_memplot_surely( "JunkPlot" , asp ) ;
3619      plotpak_pwritf( 0.01,0.5 , lab , TSIZ , 0 , -1 ) ;
3620      mp = get_active_memplot() ;
3621      bbox = memplot_bbox( mp ) ;  /* min and max x,y coords */
3622      delete_memplot( mp ) ;
3623      xsiz = bbox.b - bbox.a ; /* ysiz = bbox.d - bbox.c ; */
3624      test = (ww*xsiz)/TSIZ ;
3625      if( test > 0.97f ){         /* too wide ==> shrink font */
3626        int wwnew = (int)(TSIZ/xsiz) ;
3627        if( wwnew < ww     ) ww   = wwnew ;    /* don't go up */
3628        if( ww    < 8      ) ww   = 8 ;      /* smallest font */
3629        if( test  > 1.111f ) thk /= test ; /* thinner strokes */
3630      }
3631    }
3632 
3633    dd = 0.0007f*ww*(nlin+0.123f) ;  /* offset from edge in y direction */
3634 
3635    /* create a blank plot to be drawn into (the output from this func);
3636       this plot will be merged with other overlay plots at a later date */
3637 
3638    create_memplot_surely( "Ilabelplot" , asp ) ;
3639 
3640    set_thick_memplot(thk) ;
3641    set_opacity_memplot(1.0f) ;
3642 
3643    /* get the [initial] color to plot with */
3644    /* colors in coxplot are RGB triples, values from 0.0 to 1.0 */
3645    /* [note: the string might contain color changing commands!] */
3646 
3647    eee = getenv("AFNI_IMAGE_LABEL_COLOR") ;
3648    if( eee != NULL ) DC_parse_color( seq->dc , eee , &rr,&gg,&bb ) ;
3649    set_color_memplot(rr,gg,bb) ;
3650 
3651    /* plot the label */
3652 
3653    /** Arguments to plotpak_pwritf(xx,yy,ch,isiz,ior,icent):
3654          xx    = x coordinate of string start
3655          yy    = y coordinate of string start
3656          ch    = character string
3657          isiz  = size of characters in units of 0.001 * plot width
3658          ior   = orientation in degrees (0 = horizontal text)
3659          icent = centering code (assuming ior==0==horizontal text)):
3660                  First, the size of the box that contains the string
3661                    is computed, running from (xbot..xtop,ybot..ytop)
3662                    Note that xbot is usually 0, but ybot might be negative
3663                    due to descenders (e.g., 'y').
3664                  Second, the origin of box is moved to (xorg,yorg)
3665                    icent == -1 ==> xorg = xx+xbot           yorg = yy+(ybot+ytop)/2
3666                                ==> text centered in the y direction about yy
3667                                    text starts at xx (left justified)
3668                    icent ==  0 ==> xorg = xx+(xbot+xtop)/2  yorg = yy+(ybot+ytop)/2
3669                                    text is centered in x and y directions about (xx,yy)
3670                    icent ==  1 ==> xorg = xx+xtop           yorg = yy+(ybot+ytop)/2
3671                                ==> text centered in the y direction about yy
3672                                    text ends at xx (right justified)
3673                    icent == -3 ==> xorg = xx+max(xbot,0)    yorg = yy+max(ybot,0)
3674                                ==> text lower left corner starts at (xx,yy)
3675        Why is this so intricate and non-intuitive?
3676        It's a hangover from the original NCAR graphics code,
3677        which was code for driving pen plotters, and this library was
3678        written by the pre-Zhark RWC to emulate NCAR graphics for non-pen devices.
3679        Which also explains why these are line-drawn characters, not from fonts :( */
3680 
3681    switch( seq->wbar_label_av->ival ){
3682       default:
3683       case ISQ_LABEL_UPLF:   /* upper left */
3684          plotpak_pwritf( sb,1.0-dd-sb , lab , ww , 0 , -1 ) ; break ;
3685 
3686       case ISQ_LABEL_UPRT:   /* upper right */
3687          plotpak_pwritf( asp-sb,1.0-dd-sb , lab , ww , 0 ,  1 ) ; break ;
3688 
3689       case ISQ_LABEL_DNLF:   /* lower left */
3690          plotpak_pwritf( sb,dd+sb , lab , ww , 0 , -1 ) ; break ;
3691 
3692       case ISQ_LABEL_DNRT:   /* lower right */
3693          plotpak_pwritf( asp-sb,dd+sb , lab , ww , 0 ,  1 ) ; break ;
3694 
3695       case ISQ_LABEL_UPMD:   /* upper middle */
3696          plotpak_pwritf( 0.5*asp,1.0-dd-sb , lab , ww , 0 , 0 ) ; break ;
3697 
3698       case ISQ_LABEL_DNMD:   /* lower middle */
3699          plotpak_pwritf( 0.5*asp,dd+sb , lab , ww , 0 , 0 ) ; break ;
3700    }
3701 
3702    /** AFNI_RESTORE_XDRAWLINES ; **/
3703 
3704    mp = get_active_memplot() ; RETURN(mp) ;
3705 }
3706 
3707 /*-----------------------------------------------------------------------
3708    process an MRI_IMAGE from the user into a scaled format for display
3709    -- the output will be MRI_short (grayscale index) or MRI_rgb
3710    -- this is where the 'image processing' options from the
3711       Disp control panel are applied
3712 -------------------------------------------------------------------------*/
3713 
ISQ_process_mri(int nn,MCW_imseq * seq,MRI_IMAGE * im,int flags)3714 MRI_IMAGE * ISQ_process_mri( int nn , MCW_imseq *seq , MRI_IMAGE *im , int flags )
3715 {
3716    MRI_IMAGE *newim , *flipim , *lim ;
3717    int  scl_grp ;
3718    short clbot=0 , cltop=0 ;
3719    int must_rescale = 1 ;     /* 31 Jan 2002: always turn this on */
3720    int have_transform ;
3721    int do_0D     = (flags & PFLAG_NOTRAN0D) == 0;  /* 02 Sep 2014 */
3722    int do_2D     = (flags & PFLAG_NOTRAN2D) == 0;
3723    int do_improc = (flags & PFLAG_NOIMPROC) == 0 ;
3724    char scalestring[8];
3725 
3726 ENTRY("ISQ_process_mri") ;
3727 
3728    seq->clbot = seq->cltop = 0.0f ; /* 29 Jul 2001 */
3729 
3730    if( ! ISQ_VALID(seq) || im == NULL ) RETURN(NULL) ;
3731 
3732    /*** Feb 7, 1996: deal with complex-valued images ***/
3733 
3734    lim = im ;  /* local image = input image, unless complex */
3735 
3736    if( im->kind == MRI_complex ){
3737       float *lar ; complex *cxar ; int ii , npix ;
3738 
3739       DPRI("complex to real code = ",seq->opt.cx_code) ;
3740 
3741       lim  = mri_new( im->nx , im->ny , MRI_float ) ;
3742       lar  = MRI_FLOAT_PTR(lim) ;
3743       cxar = MRI_COMPLEX_PTR(im) ;
3744       npix = im->nx * im->ny ;
3745       MRI_COPY_AUX(lim,im) ;
3746       must_rescale = 1 ;  /** force rescaling of image later **/
3747 
3748       switch( seq->opt.cx_code ){
3749 
3750          default:
3751          case ISQ_CX_MAG:
3752             for( ii=0 ; ii < npix ; ii++ ) lar[ii] = CABS(cxar[ii]) ;
3753          break ;
3754 
3755          case ISQ_CX_PHASE:
3756             for( ii=0 ; ii < npix ; ii++ ) lar[ii] = CARG(cxar[ii]) ;
3757          break ;
3758 
3759          case ISQ_CX_REAL:
3760             for( ii=0 ; ii < npix ; ii++ ) lar[ii] = cxar[ii].r ;
3761          break ;
3762 
3763          case ISQ_CX_IMAG:
3764             for( ii=0 ; ii < npix ; ii++ ) lar[ii] = cxar[ii].i ;
3765          break ;
3766       }
3767 
3768       (void)thd_floatscan( npix , lar ) ;  /* 24 Aug 2009 */
3769    }
3770 
3771    have_transform = (seq->transform0D_func != NULL ||
3772                      seq->transform2D_func != NULL   ) && (do_0D || do_2D) ;
3773 
3774    /****** 11 Feb 1999: if input RGB image, do limited processing *****/
3775 
3776    if( lim->kind == MRI_rgb ){
3777       MRI_IMAGE *tim , *qim ;
3778 
3779       /** 26 Apr 2005: apply transforms to the intensity channel? **/
3780 
3781       if( have_transform ) qim = mri_copy( lim ) ;
3782       else                 qim = lim ;
3783 
3784       if( seq->transform0D_func != NULL && do_0D )
3785         mri_rgb_transform_nD( qim, 0, seq->transform0D_func ) ;
3786 
3787       if( seq->transform2D_func != NULL && do_2D )
3788         mri_rgb_transform_nD( qim, 2, seq->transform2D_func ) ;
3789 
3790       /** histogram flattening (very useless) **/
3791 
3792       if( (seq->opt.improc_code & ISQ_IMPROC_FLAT) != 0 ){
3793         tim = mri_flatten_rgb( qim ) ;
3794         if( qim != lim ) mri_free(qim) ;
3795         qim = tim ;
3796       }
3797 
3798       /** sharpening (sometimes useful) **/
3799 
3800       if( (seq->opt.improc_code & ISQ_IMPROC_SHARP) != 0 && do_improc ){
3801         tim = mri_sharpen_rgb( seq->sharp_fac , qim ) ;
3802         if( qim != lim ) mri_free(qim) ;
3803         qim = tim ;
3804       }
3805 
3806       /** create output:
3807            copy of input, if input was unmodified above,
3808            otherwise, the edited/filtered result from above **/
3809 
3810       if( qim == lim )
3811         newim = mri_copy( lim ) ;   /* just copy it */
3812       else
3813         newim = qim ;               /* is already what we want */
3814 
3815       /** 25 Apr 2005: modify image via rgb_gamma exponent? **/
3816 
3817       if( fabs(1.0-seq->rgb_gamma)  > 0.02 || fabs(seq->rgb_offset) > 0.01 ){
3818         register int npix = newim->nx * newim->ny , ii ;
3819         register byte *ar = MRI_RGB_PTR(newim) ;
3820         double gg = seq->rgb_gamma ;
3821         float  aa = seq->rgb_offset , rv,gv,bv , mx ;
3822 
3823         if( aa > 0.9 ) aa = 0.9; else if( aa < -0.9 ) aa = -0.9;
3824         for( ii=0 ; ii < npix ; ii++ ){
3825           if( ar[3*ii] > 0 || ar[3*ii+1] > 0 || ar[3*ii+2] > 0 ){
3826             if( aa != 0.0 ){
3827               rv = ar[3*ii]   ; gv = ar[3*ii+1] ; bv = ar[3*ii+2] ;
3828               mx = MAX(rv,gv) ; mx = (255.0*aa) / MAX(mx,bv) ;
3829               rv *= mx; gv *= mx; bv *= mx;
3830             } else {
3831               rv = gv = bv = 0.0 ;
3832             }
3833             rv += (float)(255.0*pow(ar[3*ii  ]/255.0,gg)) ;
3834             gv += (float)(255.0*pow(ar[3*ii+1]/255.0,gg)) ;
3835             bv += (float)(255.0*pow(ar[3*ii+2]/255.0,gg)) ;
3836             mx = MAX(rv,gv) ; mx = MAX(mx,bv) ;
3837             if( mx > 255.0 ){ mx = 255.0/mx; rv *= mx; gv *= mx; bv *= mx; }
3838             ar[3*ii  ] = BYTEIZE(rv) ;
3839             ar[3*ii+1] = BYTEIZE(gv) ;
3840             ar[3*ii+2] = BYTEIZE(bv) ;
3841           }
3842         }
3843       }
3844 
3845       /** save the 'original' image in float format? **/
3846 
3847       if( seq->set_orim ){                    /* for graphs */
3848         KILL_1MRI(seq->orim) ;
3849         seq->orim = mri_to_float(newim) ;    /* intensity image */
3850       }
3851 
3852       /** 11 May 2004: fill (0,0,0) pixels with zero color? **/
3853 
3854       if( seq->zer_color > 0 ){
3855         register int npix = newim->nx * newim->ny , ii ;
3856         register byte rz,gz,bz , *ar = MRI_RGB_PTR(newim) ;
3857         rz = DCOV_REDBYTE  (seq->dc,seq->zer_color) ;  /* zero  */
3858         gz = DCOV_GREENBYTE(seq->dc,seq->zer_color) ;  /* color */
3859         bz = DCOV_BLUEBYTE (seq->dc,seq->zer_color) ;  /* RGBs  */
3860         for( ii=0 ; ii < npix ; ii++ )
3861           if( ar[3*ii] == 0 && ar[3*ii+1] == 0 && ar[3*ii+2] == 0 ){
3862             ar[3*ii] = rz ; ar[3*ii+1] = gz ; ar[3*ii+2] = bz ;
3863           }
3864       }
3865 
3866       seq->scl_label[0] = '\0' ;
3867    }  /** end of RGB processing **/
3868 
3869    /****** Not RGB ==>                                             ******/
3870    /****** process image in normal fashion if no IMPROC code given ******/
3871 
3872    else if( ! have_transform && seq->opt.improc_code == ISQ_IMPROC_NONE ){
3873 
3874       if( seq->set_orim ){                   /* 30 Dec 1998 */
3875         KILL_1MRI(seq->orim) ;
3876         seq->orim = mri_to_float( lim ) ;
3877       }
3878 
3879       if( !must_rescale && ISQ_DOING_SLICE_PROJ(seq) ) must_rescale = 1 ;
3880 
3881       /*----- first, set scaling based on user desires -----*/
3882 
3883       if( nn < seq->status->num_series ){
3884         scl_grp = seq->opt.scale_group ; /* in series -> can groupscale */
3885       } else {
3886         scl_grp = ISQ_SCL_AUTO ;         /* not in series -> must autoscale */
3887       }
3888 
3889       if( seq->rng_bot < seq->rng_top ) scl_grp = ISQ_SCL_USER ;
3890 
3891       switch( scl_grp ){
3892 
3893          case ISQ_SCL_USER:{    /* scale from user input ranges */
3894            ISQ_SCLEV( seq->rng_bot,seq->rng_top ,
3895                       seq->dc->ncol_im , seq->scl,seq->lev ) ;
3896            clbot = seq->clbot = seq->rng_bot ;
3897            cltop = seq->cltop = seq->rng_top ;
3898 /*           if( seq->rng_extern ) strcpy(seq->scl_label,"[Glob]") ;*/
3899            if( seq->rng_extern ) sprintf(seq->scl_label,"[%s]",
3900               THD_get_image_globalrange_str()) ;
3901            else                  strcpy(seq->scl_label,"[User]") ;
3902          }
3903          break ; /* end of user input range scaling */
3904 
3905          default:               /* scale on individual image statistics */
3906          case ISQ_SCL_AUTO:{
3907            ISQ_indiv_statistics *st = &( seq->imstat[nn] ) ;
3908            int scrang = seq->opt.scale_range ;
3909 
3910            if( seq->top_clip <= 0.0f && scrang == ISQ_RNG_CLIPPED ){
3911              ALLOW_CLIPPING( seq , 0 ) ;
3912              scrang = seq->opt.scale_range ;
3913            }
3914 
3915            if( must_rescale ) st->one_done = False ;
3916 
3917            if( ! st->one_done ) ISQ_statify_one( seq , nn , lim ) ;
3918 
3919            /* 09 Jan 2004: adjust scaling method for image entropy */
3920 
3921            if( scrang == ISQ_RNG_02TO98 ){
3922              double ent_th=AFNI_numenv("AFNI_IMAGE_ENTROPY") ;
3923              if( ent_th >= 0.0 ){
3924                if( ent_th == 0.0 ) ent_th = 0.05 ;  /* 10 Jan 2004 */
3925                if( st->entropy < ent_th ) scrang = ISQ_RNG_MINTOMAX ;
3926              }
3927            }
3928 
3929            switch( scrang ){
3930 
3931              default:
3932              case ISQ_RNG_MINTOMAX:
3933                seq->scl = st->scl_mm ;
3934                seq->lev = st->lev_mm ;
3935                seq->clbot = st->min ;   /* 29 Jul 2001 */
3936                seq->cltop = st->max ;
3937                strcpy(seq->scl_label,"[Min2Max]") ;
3938              break ;
3939 
3940              case ISQ_RNG_02TO98:
3941                seq->scl = st->scl_per ;
3942                seq->lev = st->lev_per ;
3943                clbot = seq->clbot = st->per02 ;
3944                cltop = seq->cltop = st->per98 ;
3945                strcpy(seq->scl_label,"[2%-98%]") ;
3946              break ;
3947 
3948              case ISQ_RNG_CLIPPED:{
3949                float bf=AFNI_numenv("AFNI_IMAGE_CLIPBOT") , bc ;
3950                float tf=AFNI_numenv("AFNI_IMAGE_CLIPTOP") , tc ;
3951                if( bf < 0.0f || bf > 0.5f ) bf = 0.25f ;
3952                if( tf < 0.6f || bf > 1.9f ) tf = 1.00f ;
3953                bc = bf * seq->top_clip ;
3954                tc = tf * seq->top_clip ;
3955                ISQ_SCLEV( bc,tc ,
3956                           seq->dc->ncol_im , seq->scl,seq->lev ) ;
3957                clbot = seq->clbot = bc ;
3958                cltop = seq->cltop = tc ;
3959                strcpy(seq->scl_label,"[clipped]") ;
3960                seq->redo_clip = 1 ;
3961              }
3962              break ;
3963            }
3964          }
3965          break ;  /* end of autoscaling */
3966 
3967 #ifndef NO_GROUP_SCALE
3968          case ISQ_SCL_GRP:{         /* scale on group statistics */
3969             ISQ_glob_statistics *gl = seq->glstat ;
3970 
3971             switch( seq->opt.scale_range ){
3972 
3973                default:
3974                case ISQ_RNG_MINTOMAX:
3975                  if( ! gl->mm_done ) ISQ_statify_all( seq , True ) ;
3976                  seq->scl = gl->scl_mm ;
3977                  seq->lev = gl->lev_mm ;
3978                  seq->clbot = gl->min ;   /* 29 Jul 2001 */
3979                  seq->cltop = gl->max ;
3980                  strcpy(seq->scl_label,"[Min2Max]") ;
3981                break ;
3982 
3983                case ISQ_RNG_02TO98:
3984                  if( ! gl->per_done ) ISQ_statify_all( seq , False ) ;
3985                  seq->scl = gl->scl_per ;
3986                  seq->lev = gl->lev_per ;
3987                  clbot = seq->clbot = gl->per02 ;
3988                  cltop = seq->cltop = gl->per98 ;
3989                  strcpy(seq->scl_label,"[2%-98%]") ;
3990                break ;
3991             }
3992          }
3993          break ;  /* end of groupscaling */
3994 #endif
3995       }  /* end of scaling */
3996       floatfix(seq->clbot) ; floatfix(seq->cltop) ; /* 24 Aug 2009 */
3997 
3998       /* 11/30/94 fix: mri_to_short_sclip has problems with short overflow */
3999 
4000 #if 0
4001       if( lim->kind == MRI_short && clbot < cltop ){
4002 
4003          int npix = lim->nx * lim->ny , ii ;
4004          short *ar = MRI_SHORT_PTR(lim) ;
4005 
4006          if( seq->rng_ztop == 0 ){
4007             for( ii=0 ; ii < npix ; ii++ )
4008                     if( ar[ii] < clbot ) ar[ii] = clbot ;
4009                else if( ar[ii] > cltop ) ar[ii] = cltop ;
4010          } else {
4011             for( ii=0 ; ii < npix ; ii++ )
4012                     if( ar[ii] < clbot || ar[ii] > cltop ) ar[ii] = clbot ;
4013          }
4014 
4015       } else if( lim->kind == MRI_byte && clbot < cltop ){
4016 
4017          int npix = lim->nx * lim->ny , ii ;
4018          byte *ar = MRI_BYTE_PTR(lim) ;
4019 
4020          if( seq->rng_ztop == 0 ){
4021             for( ii=0 ; ii < npix ; ii++ )
4022                     if( ar[ii] < clbot ) ar[ii] = clbot ;
4023                else if( ar[ii] > cltop ) ar[ii] = cltop ;
4024          } else {
4025             for( ii=0 ; ii < npix ; ii++ )
4026                     if( ar[ii] < clbot || ar[ii] > cltop ) ar[ii] = clbot ;
4027          }
4028       }
4029 #endif
4030 
4031       /*----- next, scale image as defined above -----*/
4032 
4033 STATUS("scaling to shorts") ;
4034 
4035                                /* scaling   to zero   clip bot  clip top */
4036                                /* --------  --------  --------  -------- */
4037       newim = mri_to_short_sclip( seq->scl, seq->lev, seq->bot, seq->top, lim );
4038 
4039    /****** end of normal processing; handle special image processing below ******/
4040 
4041    } else {
4042       MRI_IMAGE *tim , *qim ;
4043       double scl , lev ;
4044       float hbot,htop ;
4045 
4046 STATUS("begin IMPROCessing") ;
4047 
4048       qim = lim ;  /* at the start of each process stage,
4049                       qim is the image to process;
4050                       tim is an intermediate temporary image */
4051 
4052       /***** 30 Oct 1996: transform image *****/
4053 
4054       if( seq->transform0D_func != NULL && do_0D ){
4055          tim = mri_to_float(qim) ;
4056 #if 0
4057          seq->transform0D_func( tim->nvox , MRI_FLOAT_PTR(tim) ) ;
4058 #else
4059          AFNI_CALL_0D_function( seq->transform0D_func ,
4060                                 tim->nvox , MRI_FLOAT_PTR(tim) ) ;
4061 #endif
4062          if( qim != lim ) mri_free(qim) ;
4063          qim = tim ;
4064       }
4065 
4066       if( seq->transform2D_func != NULL && do_2D ){
4067          tim = mri_to_float(qim) ;
4068 #if 0
4069          seq->transform2D_func( tim->nx , tim->ny ,
4070                                 tim->dx , tim->dy , MRI_FLOAT_PTR(tim) ) ;
4071 #else
4072          AFNI_CALL_2D_function( seq->transform2D_func ,
4073                                 tim->nx , tim->ny ,
4074                                 tim->dx , tim->dy , MRI_FLOAT_PTR(tim) ) ;
4075 #endif
4076          if( qim != lim ) mri_free(qim) ;
4077          qim = tim ;
4078       }
4079 
4080       /*** flatten ***/
4081 
4082       if( (seq->opt.improc_code & ISQ_IMPROC_FLAT) != 0 && do_improc ){
4083 STATUS("call mri_flatten") ;
4084          tim = mri_flatten( qim ) ;
4085          if( qim != lim ) mri_free(qim) ;
4086          qim = tim ;
4087 
4088          if( seq->opt.scale_range == ISQ_RNG_02TO98 &&
4089              seq->flat_top > seq->flat_bot ){
4090 
4091             float *qar = MRI_FLOAT_PTR(qim) ;
4092             int ii , npix = qim->nx * qim->ny ;
4093 
4094 STATUS("clip flattened image") ;
4095 
4096             for( ii=0 ; ii < npix ; ii++ ){
4097                     if( qar[ii] < seq->flat_bot ) qar[ii] = seq->flat_bot ;
4098                else if( qar[ii] > seq->flat_top ) qar[ii] = seq->flat_top ;
4099             }
4100          }
4101       }
4102 
4103       /*** sharpen ***/
4104 
4105       if( (seq->opt.improc_code & ISQ_IMPROC_SHARP) != 0 && do_improc ){
4106 STATUS("call mri_sharpen") ;
4107          tim = mri_sharpen( seq->sharp_fac , 0 , qim ) ;
4108          if( qim != lim ) mri_free(qim) ;
4109          qim = tim ;
4110       }
4111 
4112       /*** Sobel edge detection ***/
4113 
4114       if( (seq->opt.improc_code & ISQ_IMPROC_SOBEL) != 0 && do_improc ){
4115          int ii , npix ;
4116          float *tar ;
4117 
4118 STATUS("call mri_edit_image") ;
4119          tim = mri_edit_image( 0.10 , 1.0 , qim ) ;   /* soft clip small values */
4120          if( qim != lim ) mri_free(qim) ;
4121          qim = tim ;
4122 
4123 STATUS("call mri_sobel") ;
4124          tim  = mri_sobel( 0 , 2 , qim ) ;            /* edge detect */
4125 
4126 #if 0
4127          npix = tim->nx * tim->ny ;                   /* take square root */
4128          tar  = mri_data_pointer(tim) ;
4129          for( ii=0 ; ii < npix ; ii++ ) tar[ii] = sqrt(tar[ii]) ;
4130 #endif
4131 
4132          if( qim != lim ) mri_free(qim) ;
4133          qim = tim ;
4134       }
4135 
4136       if( seq->set_orim ){                   /* 30 Dec 1998 */
4137          KILL_1MRI(seq->orim) ;
4138          seq->orim = mri_to_float( qim ) ;
4139       }
4140 
4141       /*** scale to shorts (cf. ISQ_statify_one) ***/
4142 
4143       hbot = mri_min(qim) ; htop = mri_max(qim) ;
4144 
4145 STATUS("scale to shorts") ;
4146       switch( seq->opt.scale_range ){
4147          default:
4148          case ISQ_RNG_MINTOMAX:
4149             ISQ_SCLEV( hbot,htop , seq->dc->ncol_im , scl,lev ) ;
4150             seq->clbot = hbot ;  /* 29 Jul 2001 */
4151             seq->cltop = htop ;
4152             strcpy(seq->scl_label,"[Min2Max]") ;
4153          break ;
4154 
4155          case ISQ_RNG_02TO98:{
4156             static int hist[NHISTOG] ;
4157             float h02 , h98 ;
4158 
4159 STATUS("call mri_histogram") ;
4160             mri_histogram( qim , hbot,htop , True , NHISTOG,hist ) ;
4161 STATUS("call ISQ_perpoints") ;
4162             ISQ_perpoints( hbot,htop , hist , &h02 , &h98 ) ;
4163             ISQ_SCLEV( h02,h98 , seq->dc->ncol_im , scl,lev ) ;
4164             seq->clbot = h02 ;  /* 29 Jul 2001 */
4165             seq->cltop = h98 ;
4166             strcpy(seq->scl_label,"[2%-98%]") ;
4167          }
4168          break ;
4169 
4170          case ISQ_RNG_CLIPPED:{
4171            float bf=AFNI_numenv("AFNI_IMAGE_CLIPBOT") , bc ;
4172            float tf=AFNI_numenv("AFNI_IMAGE_CLIPTOP") , tc ;
4173            if( bf < 0.0f || bf > 0.5f ) bf = 0.25f ;
4174            if( tf < 0.6f || bf > 1.9f ) tf = 1.00f ;
4175            bc = bf * seq->top_clip ;
4176            tc = tf * seq->top_clip ;
4177            ISQ_SCLEV( bc,tc ,
4178                       seq->dc->ncol_im , scl,lev ) ;
4179            seq->clbot = bc ;
4180            seq->cltop = tc ;
4181            strcpy(seq->scl_label,"[clipped]") ;
4182            seq->redo_clip = 1 ;
4183          }
4184          break ;
4185       }
4186 
4187       newim = mri_to_short_sclip( scl , lev , seq->bot, seq->top, qim ) ;
4188       if( qim != lim ) mri_free(qim) ;
4189    }
4190 
4191    /**** at this point, the processed image is in "newim" ****/
4192    /****       and will be in short or RGB format         ****/
4193 
4194    /** 14 Jun 2010: automask 2D image **/
4195 
4196    if( MCW_val_bbox(seq->wbar_amask_bbox) > 0 ){
4197      byte *mmm = mri_automask_image2D(newim) ; MRI_IMAGE *qim ;
4198      if( mmm != NULL ){
4199        int npix = newim->nx * newim->ny , ii ;
4200        switch( newim->kind ){
4201          case MRI_short:{
4202            short zz = -seq->zer_color ;
4203            short *ar = MRI_SHORT_PTR(newim) ;
4204            for( ii=0 ; ii < npix ; ii++ )
4205              if( !mmm[ii] ) ar[ii] = zz ;
4206          }
4207          break ;
4208 
4209          case MRI_rgb:{
4210            byte *ar = MRI_RGB_PTR(newim) ;
4211            for( ii=0 ; ii < npix ; ii++ )
4212              if( !mmm[ii] ) ar[3*ii] = ar[3*ii+1] = ar[3*ii+2] = 0 ;
4213          }
4214          break ;
4215          default: break ;
4216        } /* end of switch on newim->kind */
4217        /* now save the automask for usage later (soon later) [12 Dec 2014] */
4218        KILL_1MRI(seq->last_automask) ;
4219        qim = mri_empty_conforming(newim,MRI_byte) ; mri_fix_data_pointer(mmm,qim) ;
4220        seq->last_automask = mri_flippo( ISQ_TO_MRI_ROT(seq->opt.rot) , seq->opt.mirror , qim ) ;
4221        if( qim != seq->last_automask ) mri_free(qim) ;
4222      } else {
4223        KILL_1MRI(seq->last_automask) ;
4224      }
4225    } else {
4226        KILL_1MRI(seq->last_automask) ;
4227    }
4228 
4229    /* 14 Sep 2020: invert contrast */
4230 
4231    if( MCW_val_bbox(seq->wbar_invrt_bbox) > 0 ){
4232      mri_invertcontrast_inplace( newim , 91.1f , NULL ) ;
4233    }
4234 
4235    /** Aug 31, 1995: put zer_color in at bottom, if nonzero **/
4236 
4237    if( newim->kind == MRI_short && seq->zer_color > 0 ){
4238      short zz = -seq->zer_color ;
4239      short *ar = MRI_SHORT_PTR(newim) ;
4240      int npix = newim->nx * newim->ny , ii ;
4241 
4242 #if 0
4243      for( ii=0 ; ii < npix ; ii++ )
4244        if( ar[ii] == seq->bot ) ar[ii] = zz ;   /* the olden way */
4245 #else
4246      switch( lim->kind ){                       /* the new way: 14 Jun 2010 */
4247        default: break ;
4248        case MRI_short:{
4249          short *lar = MRI_SHORT_PTR(lim) ;
4250          for( ii=0 ; ii < npix ; ii++ ) if( lar[ii] == 0 ) ar[ii] = zz ;
4251        }
4252        break ;
4253 
4254        case MRI_byte:{
4255          byte *lar = MRI_BYTE_PTR(lim) ;
4256          for( ii=0 ; ii < npix ; ii++ ) if( lar[ii] == 0 ) ar[ii] = zz ;
4257        }
4258        break ;
4259 
4260        case MRI_float:{
4261          float *lar = MRI_FLOAT_PTR(lim) ;
4262          for( ii=0 ; ii < npix ; ii++ ) if( lar[ii] == 0.0f ) ar[ii] = zz ;
4263        }
4264        break ;
4265      }
4266 #endif
4267    }
4268 
4269    /** copy pixel sizes, etc. (fixup for mrilib to be happy) **/
4270 
4271    MRI_COPY_AUX( newim , lim ) ;
4272 
4273    /*----- last, rotate/flip image to desired orientation -----*/
4274 
4275 STATUS("call mri_flippo") ;
4276    flipim = mri_flippo( ISQ_TO_MRI_ROT(seq->opt.rot) , seq->opt.mirror , newim ) ;
4277 
4278    if( newim != flipim ) KILL_1MRI(newim) ;  /* discard the trash */
4279    if( lim   != im     ) KILL_1MRI(lim) ;    /* (if there is any) */
4280 
4281    if( seq->set_orim && seq->orim != NULL ){  /* 30 Dec 1998 */
4282      MRI_IMAGE *qim ;
4283      qim = mri_flippo( ISQ_TO_MRI_ROT(seq->opt.rot), seq->opt.mirror, seq->orim ) ;
4284      if( qim != seq->orim ){ KILL_1MRI(seq->orim) ; seq->orim = qim ; } ;
4285      MRI_COPY_AUX( seq->orim , flipim ) ;
4286      seq->set_orim = 0 ;
4287    }
4288 
4289    RETURN(flipim) ;
4290 }
4291 
4292 /*-------------------------------------------------------------------
4293   Callback handlers for color palette manipulation
4294 ---------------------------------------------------------------------*/
4295 
ISQ_but_color_CB(Widget w,XtPointer client_data,XtPointer call_data)4296 void ISQ_but_color_CB( Widget w , XtPointer client_data ,
4297                                   XtPointer call_data    )
4298 {
4299    MCW_imseq *seq = (MCW_imseq *) client_data ;
4300 
4301 ENTRY("ISQ_but_color_CB") ;
4302 
4303    if( ! ISQ_REALZ(seq) ) EXRETURN ;
4304 
4305    /* change the color bar */
4306 
4307    if( seq->dc->use_xcol_im ) DC_palette_setgray( seq->dc ) ;
4308    else                       DC_palette_setcolor( seq->dc ) ;
4309 
4310    COLORMAP_CHANGE(seq) ;      /* 22 Aug 1998 */
4311    ISQ_but_done_reset( seq ) ;
4312    EXRETURN ;
4313 }
4314 
4315 /*-------------------------------------------------------------------*/
4316 /* Color bar inversion */
4317 
ISQ_but_cswap_CB(Widget w,XtPointer client_data,XtPointer call_data)4318 void ISQ_but_cswap_CB( Widget w , XtPointer client_data ,
4319                                   XtPointer call_data    )
4320 {
4321    MCW_imseq *seq = (MCW_imseq *) client_data ;
4322 
4323 ENTRY("ISQ_but_cswap_CB") ;
4324 
4325    if( ! ISQ_REALZ(seq) ) EXRETURN ;
4326 
4327    DC_palette_swap( seq->dc ) ;
4328    COLORMAP_CHANGE(seq) ;      /* 22 Aug 1998 */
4329    ISQ_but_done_reset( seq ) ;
4330    EXRETURN ;
4331 }
4332 
4333 /*-------------------------------------------------------------------
4334    Execute the image saving options, called from ISQ_but_save_CB(),
4335    which will use MCW_choose_stuff() to let the user select the
4336    parameters for the saving -- which are in array val.
4337    * nval == 2 is the Save One case  (val = prefix, blowup)
4338      nval == 3 is Save One case, but with option to invoke Gimp
4339      nval == 4 is the Save Many case (val = prefix, blowup, from, to)
4340 ---------------------------------------------------------------------*/
4341 
4342 #define POPDOWN_first_one POPDOWN_stuff_chooser
4343 
ISQ_saver_CB(Widget w,XtPointer cd,int nval,void ** val)4344 void ISQ_saver_CB( Widget w , XtPointer cd , int nval , void **val )
4345 {
4346    MCW_imseq *seq = (MCW_imseq *) cd ;
4347    int ii , kf ;
4348    MRI_IMAGE *tim , *flim ;
4349    char fname[256] ;
4350    THD_string_array *agif_list=NULL ; /* 27 Jul 2001 */
4351    char tsuf[8] ;                     /* 09 Dec 2002 */
4352    float dx,dy ;                      /* 08 Jun 2004 */
4353    int dbg ;                          /* 03 Sep 2004 */
4354    int adup=1 , akk,aa ;              /* 09 Feb 2009 */
4355 
4356    char *cval1; int ival2, ival3=0, ival4=0, ll, use_gimp=0 ; /* 26 Nov 2013 */
4357 
4358 #ifndef DONT_USE_METER
4359 #  define METER_MINCOUNT 20
4360    Widget meter = NULL ;
4361    int meter_perc , meter_pold=0 , meter_pbase ;
4362 #endif
4363 
4364 ENTRY("ISQ_saver_CB") ;
4365 
4366    if( nval < 2 || nval > 4 ) EXRETURN ;  /* bad inputs */
4367 
4368    cval1 = (char *)val[0] ;  /* copy input values to local variables */
4369    ival2 = (int)(intptr_t)val[1] ;
4370    if( nval == 4 ){
4371      ival3 = (int)(intptr_t)val[2] ;
4372      ival4 = (int)(intptr_t)val[3] ;
4373    } else if( nval == 3 ){            /* 27 Oct 2017 */
4374      char *cpt = (char *)val[2] ;
4375      use_gimp  = (cpt != NULL) && *cpt == 'Y' ;
4376    }
4377    if( cval1 == NULL || *cval1 == '\0' ) EXRETURN ;
4378    ll = strlen(cval1) ; if( ll > 32 ) EXRETURN ;
4379 
4380    dbg = AFNI_yesenv("AFNI_IMSAVE_DEBUG") ;  /* 03 Sep 2004 */
4381 
4382    /* check if we are ordered to save an animation
4383       but do not actually have the animation conversion filter */
4384 
4385    if( ppmto_agif_filter == NULL && DO_AGIF(seq) ){  /* 07 Apr 2005! */
4386       (void) MCW_popup_message( seq->wtop ,
4387                                 "Animated GIF AFNI logic error!\n"
4388                                 "Report to " COXEMAIL , MCW_USER_KILL ) ;
4389       seq->opt.save_agif = 0 ;
4390       EXRETURN ;
4391    }
4392    if( ppmto_mpeg_filter == NULL && DO_MPEG(seq) ){
4393       (void) MCW_popup_message( seq->wtop ,
4394                                 "MPEG-1 AFNI logic error!\n"
4395                                 "Report to " COXEMAIL , MCW_USER_KILL ) ;
4396       seq->opt.save_mpeg = 0 ;
4397       EXRETURN ;
4398    }
4399 
4400    /*---------------*/
4401 
4402    { /* process input prefix string */
4403 
4404       seq->saver_prefix = (char*)XtMalloc( sizeof(char) * (ll+8) ) ;
4405       strcpy( seq->saver_prefix , cval1 ) ;
4406 
4407       if( seq->saver_prefix[ll-1] != '.' ){  /* add a . at the end */
4408          seq->saver_prefix[ll++] = '.' ;     /* if one isn't there */
4409          seq->saver_prefix[ll]   = '\0' ;
4410       }
4411 
4412       /*-- check that the prefix is acceptable --*/
4413 
4414       if( dbg ) fprintf(stderr,"IMSAVE: got prefix '%s'\n",seq->saver_prefix);
4415 
4416       ll = strlen(seq->saver_prefix) ;
4417 
4418       for( ii=0 ; ii < ll ; ii++ )
4419          if( iscntrl(seq->saver_prefix[ii]) ||
4420              isspace(seq->saver_prefix[ii])   ) break ;
4421 
4422       if( ii < ll || ll < 2 || ll > 240 ){
4423          XBell( XtDisplay(w) , 100 ) ;
4424          myXtFree( seq->saver_prefix ) ; seq->saver_prefix = NULL ;
4425          EXRETURN ;
4426       }
4427 
4428       seq->saver_blowup = ival2 ;
4429 
4430       /*-- April 1996: Save One case here --*/
4431 
4432       if( nval == 2 || nval == 3 ){
4433          char *ppnm = strstr( seq->saver_prefix , ".pnm." ) ;
4434          int   sll  = strlen( seq->saver_prefix ) ;
4435 
4436          int    mcod = X2M_USE_CMAP ;        /* 21 Sep 2001: */
4437          if( seq->opt.save_filter >= 0 ||
4438              seq->mplot != NULL          )   /* compute mcod rather than */
4439            mcod |= X2M_FORCE_RGB ;           /* use fixed X2M_USE_CMAP   */
4440 
4441          /* undump XImage to MRI_IMAGE (rgb format) */
4442 
4443          if( dbg ) fprintf(stderr,"IMSAVE: convert XImage to RGB\n") ;
4444 
4445          /* we take the XImage used to draw the window
4446             and convert it back to an MRI_IMAGE for saving */
4447 
4448          reload_DC_colordef( seq->dc ) ;  /* 23 Mar 1999 */
4449          tim = XImage_to_mri( seq->dc , seq->given_xim , mcod ) ; /* 21 Sep 2001: */
4450                                                                   /* X2M_USE_CMAP -> mcod */
4451 
4452          /* save in square aspect? */
4453 
4454          if( AFNI_yesenv("AFNI_IMAGE_SAVESQUARE") ){   /* 08 Jun 2004 */
4455            tim->dx = seq->last_dx ; tim->dy = seq->last_dy ;
4456            if( dbg ) fprintf(stderr,"  square-ize aspect\n") ;
4457            flim = mri_squareaspect( tim ) ;
4458            if( flim != NULL ){ mri_free(tim); tim = flim; }
4459          }
4460 
4461          /* Apply VG filter? [20 Jun 2019] -- only for fun */
4462 
4463          { float vfac = VGFAC(seq) ;
4464            if( vfac > 0.0f ){
4465              vgize_sigfac = vfac ; flim = mri_vgize(tim) ;
4466              if( flim != NULL ){ mri_free(tim); tim = flim; }
4467            }
4468          }
4469 
4470          /* 23 Mar 2002: zoom out, if ordered */
4471 
4472          if( DO_BLOWUP(seq) && tim != NULL && tim->kind == MRI_rgb ){
4473 
4474            int zf = MAX(seq->zoom_fac,seq->saver_blowup) ;
4475            MRI_IMAGE *qim ;
4476 
4477            if( dbg ) fprintf(stderr,"  zooming\n") ;
4478            // [PT: Dec 19, 2018] Change default behavior to be NN interp.
4479            // Here and below, this condition **used** to be:
4480            // if( !AFNI_yesenv("AFNI_IMAGE_ZOOM_NN") ) mri_dup2D_mode(-7) ;
4481            if( AFNI_noenv("AFNI_IMAGE_ZOOM_NN") ) mri_dup2D_mode(-7) ;
4482            qim = mri_dup2D(zf,tim) ;
4483            mri_dup2D_mode(7) ;
4484            mri_free(tim) ; tim = qim ;
4485          }
4486 
4487          /* 23 Mar 2002: draw overlay lines on top, if any,
4488                          since they are not in the XImage, only in the window */
4489 
4490          if( tim != NULL && seq->mplot != NULL && tim->kind == MRI_rgb ){
4491            if( dbg ) fprintf(stderr,"  overlay geometry stuff\n") ;
4492            /* mri_draw_force_opaque(1) ; */
4493            memplot_to_mri_set_dothick(1) ;
4494            memplot_to_RGB_sef( tim, seq->mplot, 0,0,MEMPLOT_FREE_ASPECT ) ;
4495            memplot_to_mri_set_dothick(0) ;
4496            /* mri_draw_force_opaque(0) ; */
4497          }
4498 
4499          /* 25 Mar 2002: perhaps cut up zoomed image
4500                          (after overlay is drawn on it, that is) */
4501 
4502          if( seq->zoom_fac      > 1           &&
4503              seq->saver_blowup == 1           &&
4504              seq->mont_nx      == 1           &&
4505              seq->mont_ny      == 1           &&
4506              tim               != NULL        &&
4507              tim->kind         == MRI_rgb     &&
4508              AFNI_yesenv("AFNI_CROP_ZOOMSAVE")  ) {
4509 
4510             MRI_IMAGE *qim ;
4511             int xa,ya , iw=tim->nx/seq->zoom_fac , ih=tim->ny/seq->zoom_fac ;
4512 
4513             if( dbg ) fprintf(stderr,"  crop zoomed image\n") ;
4514             xa = seq->zoom_hor_off * tim->nx ;
4515             if( xa+iw > tim->nx ) xa = tim->nx-iw ;
4516             ya = seq->zoom_ver_off * tim->nx ;
4517             if( ya+ih > tim->ny ) ya = tim->ny-ih ;
4518             qim = mri_cut_2D( tim , xa,xa+iw-1 , ya,ya+ih-1 ) ;
4519             mri_free(tim) ; tim = qim ;
4520          }
4521 
4522          /* save image to file */
4523 
4524          if( tim != NULL ){                  /* if we have image, that is */
4525             static int warned=0 ;
4526 
4527             if( seq->opt.save_filter < 0 ){  /* the old code: dump to PNM file */
4528 
4529                if( ppnm == seq->saver_prefix + (sll-5) )  /* 17 June 1997 */
4530                   seq->saver_prefix[sll-1] = '\0' ;
4531                else
4532                   strcat(seq->saver_prefix,"pnm") ;
4533 
4534                INFO_message("Writing one %dx%d PNM image to file %s",
4535                             tim->nx,tim->ny,seq->saver_prefix) ;
4536                mri_write_pnm( seq->saver_prefix , tim ) ;
4537 
4538             } else {  /* 26 Jul 2001: allow Save One in filtered formats */
4539 
4540                char filt[512] ; int ff=seq->opt.save_filter ; FILE *fp ;
4541                int pc ;
4542 
4543                /* create the output filename with the correct suffix */
4544 
4545                sprintf(filt,".%s.",ppmto_suffix[ff]) ;
4546                if( STRING_HAS_SUFFIX_CASE(seq->saver_prefix,filt) ){
4547                  strcpy(fname,seq->saver_prefix) ;
4548                  fname[sll-1] = '\0' ;
4549                } else {
4550                  sprintf( fname, "%s%s", seq->saver_prefix, ppmto_suffix[ff] ) ;
4551                }
4552 
4553                /* create the command to do the image conversion and output */
4554 
4555                sprintf( filt , ppmto_filter[ff] , fname ) ;
4556                INFO_message("Writing one %dx%d image to file %s",
4557                             tim->nx,tim->ny,fname) ;
4558 
4559                /* open a pipe to the filter function */
4560 #ifndef CYGWIN
4561                signal( SIGPIPE , SIG_IGN ) ;  /* if the pipe fails, ignore it */
4562 #endif
4563                errno = 0 ;
4564                fp = popen( filt , "w" ) ;  /* filter command waiting for data */
4565                if( fp == NULL ){
4566                   fprintf(stderr,"** Can't open output filter: %s\a\n",filt) ;
4567                   if( errno != 0 ) perror("** Unix error message") ;
4568                   POPDOWN_first_one ;
4569                   mri_free(tim) ; EXRETURN ;
4570                }
4571 
4572                /* write a PPM file to the filter pipe */
4573 
4574                fprintf(fp,"P6\n%d %d\n255\n" , tim->nx,tim->ny ) ;
4575                fwrite( MRI_RGB_PTR(tim), sizeof(byte), 3*tim->nvox, fp ) ;
4576                pc = pclose(fp) ;   /* close the pipe */
4577                if( pc == -1 ){     /* this error should be nearly impossible */
4578                   perror("** Error in image output pipe") ;
4579                   fprintf(stderr,"** filter command was %s\n",filt) ;
4580                   POPDOWN_first_one ; mri_free(tim) ; EXRETURN ;
4581                }
4582 
4583                /* Open Gimp with this new file? [27 Oct 2017] */
4584                if( gimp_path != NULL && use_gimp && THD_is_file(fname) ){
4585                  sprintf(filt,"%s %s &",gimp_path,fname) ;
4586                  system(filt) ;
4587                }
4588             }  /* finished outputting the single image */
4589 
4590             mri_free( tim ) ; tim = NULL ;  /* 17 June 1997 */
4591 
4592             if( seq->dc->visual_class == TrueColor &&
4593                 seq->dc->depth == 16               && !warned ){ /* 30 May 2000 */
4594 
4595                warned = 1 ;      /* This can happen because colors R G B      */
4596                fprintf(stderr,   /* might not all use the same number of bits */
4597                 "\n"             /* For example, with R=B=5 bits, G=6 bits    */
4598                 "*** WARNING: Save One with X11 TrueColor depth=16 can ***\n"
4599                 "***          result in gray pixels not having R=G=B.  ***\n");
4600             }
4601 
4602          } else {
4603             XBell( XtDisplay(w) , 100 ) ;  /* image creation failed! */
4604          }
4605          myXtFree( seq->saver_prefix ) ; seq->saver_prefix = NULL ;
4606          EXRETURN ;
4607       }
4608    }
4609 
4610    /*--- save many case here ---*/
4611 
4612    seq->saver_from = ival3 ;  /* saving images from..to (inclusive) */
4613    seq->saver_to   = ival4 ;
4614 
4615    /* check if all inputs are good */
4616 
4617    if( seq->saver_prefix == NULL ||
4618        seq->saver_from < 0       ||
4619        seq->saver_to   < 0       ||
4620        seq->saver_from > seq->status->num_total-1 ||
4621        seq->saver_to   > seq->status->num_total-1   ){  /* error */
4622 
4623       XBell( XtDisplay(w) , 100 ) ;
4624       myXtFree( seq->saver_prefix ) ; seq->saver_prefix = NULL ;
4625       EXRETURN ;
4626    }
4627 
4628    if( seq->saver_from > seq->saver_to ){  /* inverted order? */
4629       ii              = seq->saver_from ;
4630       seq->saver_from = seq->saver_to ;
4631       seq->saver_to   = ii ;
4632    }
4633 
4634 #ifndef DONT_USE_METER
4635    meter_pbase = seq->saver_to - seq->saver_from ;
4636    if( meter_pbase >= METER_MINCOUNT ){
4637       meter = MCW_popup_meter( seq->wtop , METER_TOP_WIDE ) ;
4638       meter_pold = 0 ;
4639    } else {
4640       meter = NULL ;
4641    }
4642 #endif
4643 
4644    /* special stuff for animation temp files */
4645 
4646    if( DO_ANIM(seq) ){                     /* 09 Dec 2002:  */
4647      tsuf[0] = (lrand48()>>5)%26 + 'A' ;   /* random suffix */
4648      tsuf[1] = (lrand48()>>5)%26 + 'A' ;   /* for animation */
4649      tsuf[2] = (lrand48()>>5)%26 + 'A' ;   /* temp files    */
4650      tsuf[3] = '\0' ;
4651      adup = (ISQ_anim_dup > 0) ? ISQ_anim_dup : AFNI_numenv("AFNI_ANIM_DUP") ;
4652      if( adup <= 0 ) adup = 1 ; else if( adup > 99 ) adup = 99 ;
4653      if( dbg ) fprintf(stderr,"IMSAVE: animation suffix='%s' adup=%d\n",tsuf,adup) ;
4654    } else {
4655      tsuf[0] = '\0' ;                      /* not used */
4656      adup    = 1 ;
4657    }
4658 
4659 #ifdef USE_GIFF          /* create the fixed GIF colormap for animations */
4660    if( DO_AGIF(seq) ){
4661      MRI_IMAGE *im = mri_colorsetup( 76 , 6,6,5 ); /* 76 gray levels + */
4662      remove( GIFF_MAPFILE ) ;                     /* 6*red X 6*green X 5*blue */
4663      mri_write_pnm( GIFF_MAPFILE , im ) ;
4664      mri_free( im ) ;
4665    }
4666 #endif
4667 
4668    /*---------------- loop thru, get images, save them -----------------*/
4669    /*-- In this instance, we have to re-create the image as displayed --*/
4670 
4671    for( akk=0,kf=seq->saver_from ; kf <= seq->saver_to ; kf++ ){
4672 
4673       /* get the underlay image */
4674 
4675       if( dbg ) fprintf(stderr,"IMSAVE: fetch underlay image #%d\n",kf) ;
4676 
4677       tim = ISQ_getimage( kf , seq ) ;
4678 
4679       /* if we failed to get the image? */
4680 
4681       if( tim == NULL ){
4682          if( kf == seq->saver_to && agif_list != NULL ){ /* 19 Sep 2001 */
4683             fprintf(stderr,
4684                     "** Can't save animation: last image in list is NULL!\n");
4685             DESTROY_SARR(agif_list) ;
4686          }
4687          continue ;  /* skip to next one */
4688       }
4689 
4690       /* image to save will be in flim */
4691 
4692       flim = tim ;
4693 
4694 #ifndef DONT_USE_METER
4695       if( meter != NULL ){
4696         meter_perc = (int)(100.9 * (kf - seq->saver_from) / meter_pbase) ;
4697         if( meter_perc != meter_pold ){
4698           if( dbg ) fprintf(stderr,"  set meter to %d\n",meter_perc) ;
4699           MCW_set_meter( meter , meter_perc ) ;
4700           meter_pold = meter_perc ;
4701         }
4702       }
4703 #endif
4704 
4705       /*-- 27 Jun 2001: write image through a filter? --*/
4706 
4707       if( seq->opt.save_filter >= 0 || DO_ANIM(seq) ){
4708          char filt[512] ; int ff=seq->opt.save_filter ; FILE *fp ;
4709          MRI_IMAGE *ovim=NULL ;
4710          int nx , ny , npix , pc ;
4711 
4712          /* process image to make the grayscale index */
4713 
4714          if( dbg ) fprintf(stderr,"  process image\n") ;
4715 
4716          seq->set_orim = 0 ;
4717          tim  = flim ;
4718          flim = ISQ_process_mri( kf , seq , tim , 0 ) ;
4719          if( tim != flim ) KILL_1MRI( tim ) ;
4720 
4721          /* get overlay and flip it */
4722 
4723          if( !ISQ_SKIP_OVERLAY(seq) ){
4724             if( dbg ) fprintf(stderr,"  fetch overlay image\n") ;
4725             tim = ISQ_getoverlay( kf , seq ) ;
4726             if( tim != NULL && !ISQ_GOOD_OVERLAY_TYPE(tim->kind) ){
4727                KILL_1MRI(tim) ;
4728             }
4729             if( dbg ) fprintf(stderr,"  flip overlay?\n") ;
4730             if( tim != NULL )
4731               ovim = mri_flippo( ISQ_TO_MRI_ROT(seq->opt.rot), seq->opt.mirror, tim );
4732             if( tim != ovim ) KILL_1MRI(tim) ;
4733             ISQ_apply_mask( seq->last_automask , ovim ) ;
4734          }
4735 
4736          /* and perform overlay onto flim */
4737 
4738          if( ovim != NULL ){
4739             tim = flim ;
4740             if( dbg ) fprintf(stderr,"  merge overlay and underlay images\n") ;
4741             flim = ISQ_overlay( seq->dc , tim , ovim , seq->ov_opacity ) ;
4742             if( flim == NULL ){ flim = tim ; }     /* shouldn't happen */
4743             else              { KILL_1MRI(tim) ; }
4744             mri_free( ovim ) ;
4745          }
4746 
4747 /* INFO_message("AFNI_IMAGE_SAVESQUARE = %s",getenv("AFNI_IMAGE_SAVESQUARE")); */
4748          if( AFNI_yesenv("AFNI_IMAGE_SAVESQUARE") ){   /* 08 Jun 2004 */
4749            flim->dx = seq->last_dx ; flim->dy = seq->last_dy ;
4750            if( dbg ) fprintf(stderr,"  square-ize aspect ratio\n") ;
4751            tim = mri_squareaspect( flim ) ;
4752            if( tim != NULL ){ mri_free(flim); flim = tim; }
4753          }
4754 
4755          /* if needed, convert image from indices to RGB */
4756 
4757          if( flim->kind == MRI_short ){
4758            if( dbg ) fprintf(stderr,"  convert to RGB\n") ;
4759            tim = ISQ_index_to_rgb( seq->dc , 0 , flim ) ;
4760            mri_free(flim) ; flim = tim ;
4761          }
4762 
4763          /* 26 Mar 2002: zoom out, and geometry overlay, maybe */
4764 
4765          if( DO_BLOWUP(seq) ){
4766            int zf = MAX(seq->zoom_fac,seq->saver_blowup) ;
4767            if( dbg ) fprintf(stderr,"  zoom zoom zoom\n") ;
4768            // [PT: Dec 19, 2018] Change default behavior to be NN interp
4769            if( AFNI_noenv("AFNI_IMAGE_ZOOM_NN") ) mri_dup2D_mode(-7) ;
4770            tim = mri_dup2D(zf,flim) ;
4771            mri_dup2D_mode(7) ;
4772            mri_free(flim) ; flim = tim ;
4773          }
4774 
4775          if( MCW_val_bbox(seq->wbar_plots_bbox) != 0 ){  /* draw geometry overlay */
4776            MEM_plotdata *mp ;
4777            if( dbg ) fprintf(stderr,"  get geometry overlay?\n") ;
4778            mp = ISQ_getmemplot( kf , seq ) ;
4779            if( mp != NULL ){
4780              if( dbg ) fprintf(stderr,"  perform geometry overlay\n") ;
4781              flip_memplot( ISQ_TO_MRI_ROT(seq->opt.rot),seq->opt.mirror,mp );
4782              /* mri_draw_force_opaque(1) ; */
4783              memplot_to_RGB_sef( flim, mp, 0,0,MEMPLOT_FREE_ASPECT ) ;
4784              /* mri_draw_force_opaque(0) ; */
4785              delete_memplot(mp) ;
4786            }
4787          }
4788 
4789          if( seq->wbar_label_av->ival != 0 ){  /* Label overlay - 17 Jun 2005 */
4790            char *lab = ISQ_getlabel( kf , seq ) ;
4791            if( lab != NULL ){
4792              MEM_plotdata *mp = ISQ_plot_label( seq , lab ) ;
4793              if( mp != NULL ){
4794                /* mri_draw_force_opaque(1) ; */
4795                memplot_to_mri_set_dothick(1) ;
4796                memplot_to_RGB_sef( flim, mp, 0,0,MEMPLOT_FREE_ASPECT ) ;
4797                memplot_to_mri_set_dothick(0) ;
4798                /* mri_draw_force_opaque(0) ; */
4799                delete_memplot(mp) ;
4800              }
4801              free(lab) ;
4802            }
4803          }
4804 
4805          if( seq->zoom_fac      > 1 &&    /* crop zoomed image */
4806              seq->saver_blowup == 1 &&
4807              seq->mont_nx      == 1 &&    /* to displayed part? */
4808              seq->mont_ny      == 1 &&
4809              AFNI_yesenv("AFNI_CROP_ZOOMSAVE") ) {
4810 
4811            int xa,ya , iw=flim->nx/seq->zoom_fac , ih=flim->ny/seq->zoom_fac ;
4812 
4813            if( dbg ) fprintf(stderr,"  crop zoomed image\n") ;
4814            xa = seq->zoom_hor_off * flim->nx ;
4815            if( xa+iw > flim->nx ) xa = flim->nx-iw ;
4816            ya = seq->zoom_ver_off * flim->nx ;
4817            if( ya+ih > flim->ny ) ya = flim->ny-ih ;
4818            tim = mri_cut_2D( flim , xa,xa+iw-1 , ya,ya+ih-1 ) ;
4819            if( tim != NULL ){ mri_free(flim); flim = tim; }
4820          }
4821 
4822          /* image dimensions we are saving */
4823 
4824          nx = flim->nx ; ny = flim->ny ; npix = nx*ny ;
4825 
4826          /* write the output file */
4827 
4828          if( !DO_ANIM(seq) ){   /* don't write progress for animation */
4829            if( kf == seq->saver_from )
4830               printf("writing %d x %d .%s files",nx,ny,ppmto_suffix[ff]) ;
4831            else if( kf%10 == 5 )
4832               printf("." ) ;
4833            fflush(stdout) ;
4834          }
4835 
4836          /* create the filter command into string 'filt' */
4837 
4838          for( aa=0 ; aa < adup ; aa++,akk++ ){ /* adup==1 if no animation */
4839            if( !DO_ANIM(seq) ){                          /* arbitrary filtering */
4840              sprintf( fname, "%s%04d.%s", seq->saver_prefix, kf, ppmto_suffix[ff] ) ;
4841              sprintf( filt , ppmto_filter[ff] , fname ) ;
4842            } else if( DO_AGIF(seq) ){                    /* use the gif filter */
4843              sprintf( fname, "%s%s.%05d.gif" , seq->saver_prefix,tsuf, akk) ;
4844 #ifndef USE_GIFF
4845              sprintf( filt , ppmto_gif_filter  , fname ) ;  /* free colormap */
4846 #else
4847              sprintf( filt , ppmto_giff_filter , fname ) ;  /* fixed colormap */
4848 #endif
4849              if( agif_list == NULL ) INIT_SARR(agif_list) ;
4850              ADDTO_SARR(agif_list,fname) ;
4851            } else if( DO_MPEG(seq) ){                    /* use the ppm filter */
4852              sprintf( fname, "%s%s.%06d.ppm" , seq->saver_prefix,tsuf, akk) ;
4853              sprintf( filt , ppmto_ppm_filter , fname ) ;
4854              if( agif_list == NULL ) INIT_SARR(agif_list) ;
4855              ADDTO_SARR(agif_list,fname) ;
4856            }
4857 #ifndef CYGWIN
4858            signal( SIGPIPE , SIG_IGN ) ;                 /* ignore broken pipe */
4859 #endif
4860            if( dbg ) fprintf(stderr,"  piping image to '%s'\n",filt) ;
4861            fp = popen( filt , "w" ) ;                    /* open pipe to filter */
4862            if( fp == NULL ){                             /* should not happen */
4863              ERROR_message("Can't open output filter %s",filt) ;
4864              break ;  /* out of loop over aa */
4865            }
4866 
4867            /* write RGB image to pipe as a PPM file */
4868 
4869            fprintf(fp,"P6\n%d %d\n255\n" , nx,ny ) ;
4870            fwrite( MRI_RGB_PTR(flim), sizeof(byte), 3*npix, fp ) ;
4871            pc = pclose(fp) ;
4872            if( pc == -1 ) perror("Error in image output pipe") ;
4873            if( dbg ) fprintf(stderr,"  pipe done\n") ;
4874          } /* loop over aa = image duplicates for animations */
4875 
4876          /* done with this image */
4877 
4878          mri_free(flim) ; flim = NULL ;
4879 
4880          /* 27 Jul 2001: if doing animation,
4881                          and if at final image, then create result */
4882 
4883          if( kf == seq->saver_to && agif_list != NULL ){
4884 
4885             int af ;
4886 
4887             if( agif_list->num == 0 ){
4888                ERROR_message("Can't save animation: no images in list!");
4889                goto AnimationCleanup ;
4890             }
4891 
4892             /* animated GIF */
4893 
4894             if( DO_AGIF(seq) ){
4895                int alen ; char *alc , *alf , *oof ;
4896 #ifdef USE_GIFF
4897                remove( GIFF_MAPFILE ) ;   /* don't need this any longer */
4898 #endif
4899 
4900                for( alen=af=0 ; af < agif_list->num ; af++ ) /* size of all */
4901                   alen += strlen( agif_list->ar[af] ) ;      /* filename strings */
4902 
4903                alen += 3*agif_list->num + 32 ;               /* all filenames */
4904                alc = AFMALL ( char, alen) ; alc[0] = '\0' ;  /* in one string */
4905                for( alen=af=0 ; af < agif_list->num ; af++ ){
4906                  strcat(alc," ") ; strcat(alc,agif_list->ar[af]) ;
4907                }
4908 
4909                oof  = AFMALL( char, strlen(seq->saver_prefix)+32 ) ; /* output fname */
4910                sprintf(oof,"%sgif",seq->saver_prefix) ;
4911 
4912                alen =  strlen(alc)+strlen(ppmto_agif_filter)+strlen(oof)+32 ;
4913                alf  = AFMALL( char, alen) ;
4914                sprintf(alf , ppmto_agif_filter, alc, oof ) ; /* command to run */
4915                INFO_message("Running '%s'\n",alf) ;
4916                system(alf) ;                                 /* so run it!    */
4917                free(alf) ; free(oof) ; free(alc) ;           /* free trash   */
4918             }
4919 
4920             /* MPEG-1 */
4921 
4922             else if( DO_MPEG(seq) ){ /* 02 Aug 2001 */
4923                int alen ; char *alf , *oof , *frate ;
4924                char *qscale , *pattrn ;
4925 
4926                /* compose ffmpeg parameters */
4927                oof = AFMALL( char, strlen(seq->saver_prefix)+32 ) ; /* output fname */
4928                sprintf(oof,"%smpg",seq->saver_prefix) ;
4929                qscale=getenv("AFNI_MPEG_QSCALE")   ; if(qscale==NULL) qscale="11" ;
4930                frate =getenv("AFNI_MPEG_FRAMERATE"); if(frate ==NULL) frate ="24" ;
4931                pattrn=getenv("AFNI_MPEG_PATTERN")  ;
4932                if( pattrn == NULL ){                  /* 07 Apr 2009 */
4933                  /* by default use only I-frames */
4934                  if( adup <= 1 ) pattrn="-intra";
4935                  /* otherwise go with ffmpegs default */
4936                  else pattrn="";
4937                }
4938                /* make command to run */
4939                alen = strlen(seq->saver_prefix) + strlen(ppmto_mpeg_filter)
4940                       +1000 ;
4941                alf  = AFMALL( char, alen) ;
4942                /* output fps default to 25 */
4943                sprintf(alf,
4944                  "%s -r %s -f image2 -i %s%s.%%06d.ppm -b 400k -qscale %s %s %s",
4945                  ppmto_mpeg_filter, frate, seq->saver_prefix, tsuf, qscale,
4946                  pattrn, oof) ; /* command to run */
4947                INFO_message("Running '%s' to produce %s\n",alf,oof) ;
4948                system(alf) ;                            /* so run it!    */
4949                free(alf); free(oof); /* free trash   */
4950             }
4951 
4952             /* animation is done, for good or for ill */
4953 
4954             for( af=0 ; af < agif_list->num ; af++ )  /* erase temp files */
4955               remove( agif_list->ar[af] ) ;
4956 
4957           AnimationCleanup:
4958             DESTROY_SARR(agif_list) ;                 /* free more trash */
4959          }
4960       }
4961 
4962       /*---------------*/
4963 
4964       else if( flim->kind == MRI_rgb ){ /* 11 Feb 1998: write color image */
4965                                         /*              directly as PPM   */
4966          if( kf == seq->saver_from )
4967             printf("writing %d x %d RGB images",flim->nx,flim->ny) ;
4968          else if( kf%10 == 5 )
4969             printf("." ) ;
4970          fflush(stdout) ;
4971 
4972          seq->set_orim = 0 ;  /* 30 Dec 1998 */
4973          tim  = flim ;
4974          flim = ISQ_process_mri( kf , seq , tim , 0 ) ;  /* image processing */
4975          if( tim != flim ) KILL_1MRI( tim ) ;
4976 
4977 /* INFO_message("AFNI_IMAGE_SAVESQUARE = %s",getenv("AFNI_IMAGE_SAVESQUARE")); */
4978          if( AFNI_yesenv("AFNI_IMAGE_SAVESQUARE") ){   /* 08 Jun 2004 */
4979            flim->dx = seq->last_dx ; flim->dy = seq->last_dy ;
4980            if( dbg ) fprintf(stderr,"  square-ate aspect ratio\n") ;
4981            tim = mri_squareaspect( flim ) ;
4982            if( tim != NULL ){ mri_free(flim); flim = tim; }
4983          }
4984 
4985          sprintf( fname , "%s%04d.pnm" , seq->saver_prefix , kf ) ;
4986          mri_write_pnm( fname , flim ) ;
4987 
4988          mri_free(flim) ; flim = NULL ; /* done with this image */
4989 
4990       /*---------------*/
4991 
4992       } else if( ! seq->opt.save_pnm ){ /** write background only **/
4993 
4994          if( seq->opt.save_nsize ){
4995            tim = mri_nsize( flim ) ;
4996            if( tim != NULL && tim != flim ){ mri_free(flim) ; flim = tim ; }
4997          }
4998 
4999          tim  = flim ;
5000          flim = mri_flippo( ISQ_TO_MRI_ROT(seq->opt.rot) , seq->opt.mirror , tim ) ;
5001          if( tim != flim ) KILL_1MRI( tim ) ;
5002 
5003          if( kf == seq->saver_from )
5004             printf("writing %d x %d images",flim->nx,flim->ny) ;
5005          else if( kf%10 == 5 )
5006             printf("." ) ;
5007          fflush(stdout) ;
5008 
5009          if( flim->kind == MRI_byte ){  /* 17 Feb 1999 */
5010             sprintf( fname , "%s%04d.pnm" , seq->saver_prefix , kf ) ;
5011             mri_write_pnm( fname , flim ) ; mri_free( flim ) ; flim = NULL ;
5012          } else {
5013             sprintf( fname , "%s%04d" , seq->saver_prefix , kf ) ;
5014             mri_write( fname , flim ) ; mri_free( flim ) ; flim = NULL ;
5015          }
5016 
5017       /*---------------*/
5018 
5019       } else { /** write color overlay and everything **/
5020 
5021          MRI_IMAGE *ovim=NULL ;
5022          int ii , nx , ny , npix , bb , allgray , ncode,nout ;
5023          byte *rgb ;   /* "byte" is defined in mrilib.h */
5024          short *flar ;
5025          XColor *ulc , *ovc , *xc ;
5026          FILE *fd ;
5027          byte rrr,ggg,bbb ;
5028 
5029          /* process given image to make the grayscale index */
5030 
5031          seq->set_orim = 0 ;  /* 30 Dec 1998 */
5032          tim  = flim ;
5033          flim = ISQ_process_mri( kf , seq , tim , 0 ) ;  /* will be shorts now */
5034          if( tim != flim ) KILL_1MRI( tim ) ;
5035 
5036          flar = mri_data_pointer(flim) ;  /* underlay image data */
5037          nx = flim->nx ;
5038          ny = flim->ny ; npix = flim->nx * flim->ny ;
5039 
5040          /* get overlay and flip it */
5041 
5042          if( !ISQ_SKIP_OVERLAY(seq) ){
5043             tim = ISQ_getoverlay( kf , seq ) ;
5044             if( tim != NULL && !ISQ_GOOD_OVERLAY_TYPE(tim->kind) ){
5045                KILL_1MRI(tim) ;
5046             }
5047             if( tim != NULL )
5048               ovim = mri_flippo( ISQ_TO_MRI_ROT(seq->opt.rot), seq->opt.mirror, tim ) ;
5049             if( tim != ovim ) KILL_1MRI(tim) ;
5050             ISQ_apply_mask( seq->last_automask , ovim ) ;
5051          }
5052 
5053          /* perform overlay onto flim [modified 07 Mar 2001] */
5054 
5055          if( ovim != NULL ){
5056             tim = flim ;
5057             flim = ISQ_overlay( seq->dc , tim , ovim , seq->ov_opacity ) ;
5058             if( flim == NULL ){ flim = tim ; }     /* shouldn't happen */
5059             else              { KILL_1MRI(tim) ; }
5060             mri_free( ovim ) ;
5061          }
5062 
5063 /* INFO_message("AFNI_IMAGE_SAVESQUARE = %s",getenv("AFNI_IMAGE_SAVESQUARE")); */
5064          if( AFNI_yesenv("AFNI_IMAGE_SAVESQUARE") ){   /* 08 Jun 2004 */
5065            flim->dx = seq->last_dx ; flim->dy = seq->last_dy ;
5066            tim = mri_squareaspect( flim ) ;
5067            if( tim != NULL ){ mri_free(flim); flim = tim; }
5068          }
5069 
5070          /* write the output file */
5071 
5072          if( kf == seq->saver_from )
5073             printf("writing %d x %d PNM files",nx,ny) ;
5074          else if( kf%10 == 5 )
5075             printf("." ) ;
5076          fflush(stdout) ;
5077 
5078          sprintf( fname , "%s%04d.pnm" , seq->saver_prefix , kf ) ;
5079 
5080          if( flim->kind == MRI_rgb ){                        /* 07 Mar 2001 */
5081             mri_write_pnm( fname , flim ) ; mri_free(flim) ; flim = NULL ;
5082          } else {                                            /* the old way */
5083 
5084             /* XColor arrays for underlay and overlay */
5085 
5086             ulc = ( seq->dc->use_xcol_im ) ? seq->dc->xcol_im
5087                                            : seq->dc->xgry_im ;
5088             ovc = seq->dc->ovc->xcol_ov ;
5089 
5090             fd = fopen( fname , "r" ) ;
5091             if( fd != NULL ){
5092                fclose(fd) ;
5093                fprintf(stderr,"(FAILED) attempt to overwrite file %s\n",fname) ;
5094                continue ;
5095             }
5096             fd = fopen( fname , "w" ) ;
5097             if( fd == NULL ){
5098                fprintf(stderr,"couldn't open output file %s\n",fname) ;
5099                continue ;
5100             }
5101 
5102             /* write the XColor intensities into the output */
5103 
5104             rgb = (byte *) XtMalloc( sizeof(byte) * 3 * npix ) ;
5105             bb  = 0 ;
5106 
5107             allgray = 1 ;  /* June 1995: check if all are gray */
5108 
5109             flar = mri_data_pointer(flim) ;  /* underlay image data */
5110 
5111             for( ii=0 ; ii < npix ; ii++ ){
5112                xc  = (flar[ii] >= 0) ? (ulc+flar[ii]) : (ovc-flar[ii]) ;
5113                rrr = rgb[bb++] = INTEN_TO_BYTE( xc->red ) ;
5114                ggg = rgb[bb++] = INTEN_TO_BYTE( xc->green ) ;
5115                bbb = rgb[bb++] = INTEN_TO_BYTE( xc->blue ) ;
5116 
5117                if( allgray ) allgray = ((rrr==ggg) && (ggg==bbb)) ;
5118             }
5119 
5120             /* if all are gray, compress to a PGM, else leave as a PPM */
5121 
5122             if( allgray ){
5123                bb = 3 ;
5124                for( ii=1 ; ii < npix ; ii++ ){ rgb[ii] = rgb[bb] ; bb += 3 ; }
5125                ncode = 5 ;     /* PGM */
5126                nout  = npix ;
5127             } else {
5128                ncode = 6 ;     /* PPM */
5129                nout  = 3*npix ;
5130             }
5131 
5132             fprintf(fd,"P%d\n%d %d\n255\n",ncode,nx,ny) ; /* write PNM header */
5133             fwrite( rgb , sizeof(byte) , nout , fd ) ;         /* write bytes */
5134             fclose( fd ); mri_free(flim); flim = NULL; myXtFree(rgb); /* DONE */
5135          }
5136       }
5137    } /* end of loop over images */
5138 
5139    printf(". **DONE**\n") ; fflush(stdout) ;
5140 
5141    /*--- go home ---*/
5142 
5143 #ifndef DONT_USE_METER
5144    if( meter != NULL ) MCW_popdown_meter(meter) ;
5145 #endif
5146 
5147    myXtFree( seq->saver_prefix ) ; seq->saver_prefix = NULL ;
5148    EXRETURN ;
5149 }
5150 
5151 
5152 /*----------------------------------------------------------------------*/
5153 /*! Called from the 'Save' button; starts the save image dialog. */
5154 
ISQ_but_save_CB(Widget w,XtPointer client_data,XtPointer call_data)5155 void ISQ_but_save_CB( Widget w , XtPointer client_data ,
5156                                  XtPointer call_data    )
5157 {
5158    MCW_imseq *seq = (MCW_imseq *) client_data ;
5159    int ibl = (seq->saver_blowup > 0 && seq->saver_blowup < 9 ) ? seq->saver_blowup : 1 ;
5160 
5161 ENTRY("ISQ_but_save_CB") ;
5162 
5163    if( ! ISQ_REALZ(seq) || w == NULL || ! XtIsWidget(w) ) EXRETURN ;
5164 
5165    seq->saver_prefix = NULL ;
5166    seq->saver_from = seq->saver_to = -1 ;
5167 
5168    /* popup a chooser to choose 'stuff' = items of different kinds */
5169 
5170    if( seq->opt.save_one && !DO_ANIM(seq) ){
5171      if( seq->opt.save_filter >= 0 &&
5172          ppmto_gimpize != NULL     && ppmto_gimpize[seq->opt.save_filter] ){
5173        MCW_choose_stuff( w , "Image Saver (One)" , ISQ_saver_CB , (XtPointer)seq ,
5174                            MSTUF_STRING , "Prefix"  ,
5175                            MSTUF_INT    , "Blowup " , 1 , 8 , ibl ,
5176                            MSTUF_YESNO  , "Open in Gimp?",
5177                          MSTUF_END ) ;
5178      } else {
5179        MCW_choose_stuff( w , "Image Saver (One)" , ISQ_saver_CB , (XtPointer)seq ,
5180                            MSTUF_STRING , "Prefix"  ,
5181                            MSTUF_INT    , "Blowup " , 1 , 8 , ibl ,
5182                          MSTUF_END ) ;
5183      }
5184    } else {
5185      MCW_choose_stuff( w , "Image Saver (Multiple)" , ISQ_saver_CB , (XtPointer)seq ,
5186                          MSTUF_STRING , "Prefix"  ,
5187                          MSTUF_INT    , "Blowup " , 1 , 8 , ibl ,
5188                          MSTUF_INT    , "From:  " , 0 , seq->status->num_total-1 , 0 ,
5189                          MSTUF_INT    , "To:    " , 0 , seq->status->num_total-1 , seq->status->num_total-1 ,
5190                        MSTUF_END ) ;
5191    }
5192 
5193    ISQ_but_done_reset( seq ) ;
5194    EXRETURN ;
5195 }
5196 
5197 /*------------------------------------------------------------------------
5198    Set the "DONE" button back to be the "Done" button
5199 --------------------------------------------------------------------------*/
5200 
5201 #ifdef REQUIRE_TWO_DONES
ISQ_but_done_reset(MCW_imseq * seq)5202 void ISQ_but_done_reset( MCW_imseq *seq )
5203 {
5204    if( ! ISQ_VALID(seq) || seq->done_first ) return ;
5205 
5206    MCW_set_widget_label( seq->wbut_bot[NBUT_DONE] , ISQ_but_done_label1 ) ;
5207    seq->done_first = True ;
5208    return ;
5209 }
5210 #endif
5211 
5212 /*-----------------------------------------------------------------------
5213    Deletion of an imseq from the Macrocosmic All
5214 -------------------------------------------------------------------------*/
5215 
ISQ_but_done_CB(Widget w,XtPointer client_data,XtPointer call_data)5216 void ISQ_but_done_CB( Widget w , XtPointer client_data ,
5217                                  XtPointer call_data    )
5218 {
5219    MCW_imseq *seq = (MCW_imseq *)client_data ;
5220 
5221 ENTRY("ISQ_but_done_CB") ;
5222 
5223    if( ! ISQ_VALID(seq) ) EXRETURN ;
5224 
5225 #ifdef REQUIRE_TWO_DONES
5226    /*-- first call from "Done" button --> change label, return */
5227 
5228    if( w == seq->wbut_bot[NBUT_DONE] && seq->done_first ){
5229       MCW_set_widget_label( w , ISQ_but_done_label2 ) ;
5230       seq->done_first = False ;
5231       EXRETURN ;
5232    }
5233 #endif
5234 
5235    /*-- second call: kill --*/
5236 
5237 #ifdef AUTOMATE_STATISTICS  /* no longer allowed */
5238    if( seq->glstat->worker != 0 ){  /* remove work process, if started */
5239      XtRemoveWorkProc( seq->glstat->worker ) ;
5240      seq->glstat->worker = 0 ;
5241    }
5242 #endif
5243 
5244    ISQ_timer_stop(seq) ;
5245 
5246    if( seq->dialog != NULL ){ /* 13 Aug 2002 */
5247      XtDestroyWidget( seq->dialog ) ; NI_sleep(1) ;
5248    }
5249 
5250    ISQ_free_alldata( seq ) ;
5251    XtDestroyWidget( seq->wtop ) ; NI_sleep(3) ;
5252    seq->valid = 0 ;     /* WE do not deallocate the data structure! */
5253 
5254    STATUS("IMSEQ: data destroyed!") ;
5255 
5256    if( seq->status->send_CB != NULL ){
5257       ISQ_cbs cbs ;
5258 
5259       STATUS("IMSEQ: sending destroy message") ;
5260 
5261       cbs.reason = isqCR_destroy ;
5262 #if 0
5263       seq->status->send_CB( seq , seq->getaux , &cbs ) ;
5264 #else
5265       SEND(seq,cbs) ;
5266 #endif
5267    }
5268 
5269    EXRETURN ;
5270 }
5271 
5272 /*-----------------------------------------------------------------------
5273    delete malloc-ed data in an imseq
5274 -------------------------------------------------------------------------*/
5275 
ISQ_free_alldata(MCW_imseq * seq)5276 void ISQ_free_alldata( MCW_imseq *seq )
5277 {
5278    int ib ;
5279 
5280 ENTRY("ISQ_free_alldata") ;
5281 
5282    if( ! ISQ_VALID(seq) ) EXRETURN ;
5283 
5284    KILL_1MRI( seq->imim ) ;
5285    KILL_1MRI( seq->ovim ) ;
5286    KILL_1MRI( seq->orim ) ;  /* 30 Dec 1998 */
5287    KILL_1MRI( seq->last_automask ) ; /* 12 Dec 2014 */
5288 
5289    KILL_2XIM( seq->given_xim  , seq->sized_xim  ) ;
5290    KILL_2XIM( seq->given_xbar , seq->sized_xbar ) ;
5291 
5292    myXtFree( seq->imstat ) ; seq->imstat = NULL ;
5293    myXtFree( seq->glstat ) ; seq->glstat = NULL ;
5294 
5295    for( ib=0 ; ib < seq->num_bbox ; ib++ )
5296       myXtFree( seq->bbox[ib] ) ;
5297    seq->num_bbox = 0 ;
5298 
5299    for( ib=0 ; ib < NARROW ; ib++ ) myXtFree( seq->arrow[ib] ) ;
5300 
5301    myXtFree( seq->arrowpad )           ;
5302    FREE_AV( seq->mont_across_av )     ;
5303    FREE_AV( seq->mont_down_av )       ;
5304    FREE_AV( seq->mont_skip_av )       ;
5305    FREE_AV( seq->mont_gap_av )        ;
5306    FREE_AV( seq->mont_gapcolor_av )   ;
5307    FREE_AV( seq->mont_type_av )       ;
5308    FREE_AV( seq->transform0D_av )     ; /* 30 Oct 1996 */
5309    FREE_AV( seq->transform2D_av )     ;
5310    FREE_AV( seq->rowgraph_av )        ; /* 30 Dec 1998 */
5311    FREE_AV( seq->surfgraph_av )       ; /* 21 Jan 1999 */
5312    myXtFree( seq->surfgraph_arrowpad );
5313    FREE_AV( seq->ov_opacity_av )      ; /* 07 Mar 2001 */
5314    FREE_AV( seq->wbar_label_av )      ; /* 20 Sep 2001 */
5315    myXtFree( seq->wbar_plots_bbox )   ;
5316 
5317    FREE_AV( seq->wbar_labsz_av )      ; /* 06 Jan 2005: oopsie */
5318    myXtFree( seq->pen_bbox ) ;          /* 06 Jan 2005: oopsie again */
5319 
5320    FREE_AV( seq->slice_proj_av )      ; /* 31 Jan 2002 */
5321    FREE_AV( seq->slice_proj_range_av );
5322 
5323    FREE_AV( seq->wbar_ticnum_av )     ; /* 23 Feb 2004 */
5324    FREE_AV( seq->wbar_ticsiz_av )     ; /* 23 Feb 2004 */
5325 
5326    FREE_AV( seq->zoom_val_av ) ;
5327    if( seq->zoom_pixmap != (Pixmap) 0 ){
5328      XFreePixmap( seq->dc->display , seq->zoom_pixmap ) ;
5329      seq->zoom_pixmap = (Pixmap) 0 ;
5330    }
5331    MCW_kill_XImage( seq->zoom_xim ) ; seq->zoom_xim = NULL ;
5332 
5333    if( seq->rowgraph_mtd != NULL ){                /* 30 Dec 1998 */
5334      seq->rowgraph_mtd->killfunc = NULL ;
5335      plotkill_topshell( seq->rowgraph_mtd ) ;
5336    }
5337 
5338    if( seq->surfgraph_mtd != NULL ){               /* 21 Jan 1999 */
5339      seq->surfgraph_mtd->killfunc = NULL ;
5340      plotkill_topshell( seq->surfgraph_mtd ) ;
5341    }
5342 
5343    if( seq->graymap_mtd != NULL ){                 /* 24 Oct 2003 */
5344      seq->graymap_mtd->killfunc = NULL ;
5345      plotkill_topshell( seq->graymap_mtd ) ;
5346    }
5347 
5348 #if 0
5349    myXtFree(seq->status) ;                         /* 05 Feb 2000 */
5350 #endif
5351 
5352    /* 24 Apr 2001: destroy any recorded images */
5353 
5354                    /* 05 Jan 2005: include the memplot recordings */
5355    if( seq->record_mplot != NULL && seq->record_imarr != NULL ){
5356      for( ib=0 ; ib < IMARR_COUNT(seq->record_imarr) ; ib++ )
5357        delete_memplot( seq->record_mplot[ib] ) ;
5358      free((void *)seq->record_mplot) ; seq->record_mplot = NULL ;
5359    }
5360    if( seq->record_imarr != NULL ) DESTROY_IMARR(seq->record_imarr) ;
5361    if( seq->record_imseq != NULL )
5362       drive_MCW_imseq( seq->record_imseq , isqDR_destroy , NULL ) ;
5363 
5364    myXtFree( seq->record_status_bbox ) ;
5365    myXtFree( seq->record_method_bbox ) ;
5366 
5367    if( seq->mplot != NULL ){                       /* 19 Sep 2001 */
5368       delete_memplot( seq->mplot ); seq->mplot = NULL;
5369    }
5370 
5371    if( seq->overlay_label != NULL ){               /* 23 Dec 2011 */
5372      free(seq->overlay_label) ; seq->overlay_label = NULL ;
5373    }
5374 
5375    EXRETURN ;
5376 }
5377 
5378 /*----------------------------------------------------------------------
5379   callback when the slider (scale) is moved
5380 ------------------------------------------------------------------------*/
5381 
ISQ_scale_CB(Widget w,XtPointer client_data,XtPointer call_data)5382 void ISQ_scale_CB( Widget w , XtPointer client_data , XtPointer call_data )
5383 {
5384    MCW_imseq *seq             = (MCW_imseq *)             client_data ;
5385    XmScaleCallbackStruct *cbs = (XmScaleCallbackStruct *) call_data ;
5386 
5387 ENTRY("ISQ_scale_CB") ;
5388 
5389    if( ! ISQ_REALZ(seq) ) EXRETURN ;
5390 
5391    if( seq->status->num_total < 2 ){  /* 29 Jul 2002 */
5392       XmScaleSetValue( seq->wscale , 0 ) ;
5393       EXRETURN ;
5394    }
5395 
5396    ISQ_redisplay( seq , cbs->value , isqDR_display ) ;
5397 
5398    ISQ_but_done_reset( seq ) ;
5399    EXRETURN ;
5400 }
5401 
5402 /*-----------------------------------------------------------------------
5403   Redo the display for a particular image:
5404      n < 0 , type = isqDR_display ==> redisplay current image and overlay
5405              type = isqDR_overlay ==> redisplay current overlay only
5406              type = isqDR_reimage ==> redisplay current image only
5407              type = isqDR_reshow  ==> just reshow (same as ISQ_show_image)
5408 
5409      n >= 0, type = isqDR_display ==> redisplay image n and overlay n
5410              type = isqDR_overlay ==> if current image is n, just
5411                                         redisplay overlay n, otherwise both
5412              type = isqDR_reimage ==> if current image is n, just
5413                                         redisplay image n, otherwise both
5414 -------------------------------------------------------------------------*/
5415 
5416 /***
5417   Modified Mar 25 1996:
5418     If the image number scale is moved, then this routine is called,
5419     and then ISQ_set_image_number is called, which then calls the
5420     send_CB callback, which may end up calling this routine again
5421     (via drive_MCW_imseq -- for example, see AFNI_seq_send_CB).
5422     This will result in redisplaying the desired image twice, with
5423     the resulting speed penalty.  To prevent this, ISQ_redisplay
5424     now checks if the call is recursive.  If it is recursive, and
5425     it is being called with the same seq and n parameters as before,
5426     the routine exits.
5427 ***/
5428 
5429 #define RECUR (recur_flg && seq == recur_seq && n == recur_n)
5430 
ISQ_redisplay(MCW_imseq * seq,int n,int type)5431 void ISQ_redisplay( MCW_imseq *seq , int n , int type )
5432 {
5433    RwcBoolean kill_im , kill_ov ;
5434    int nrold ;
5435    static int        recur_flg = FALSE ;
5436    static int        recur_n   = -1 ;
5437    static MCW_imseq *recur_seq = NULL ;
5438 
5439    if( seq == NULL || seq->ignore_redraws ) return ;  /* 16 Aug 2002 */
5440 ENTRY("ISQ_redisplay") ;
5441 
5442    if( ! ISQ_VALID(seq) ) EXRETURN ;
5443 
5444    /** check for identical recursive call **/
5445 
5446    if( RECUR ){
5447      DPRI("ABORTED FOR RECURSION at n =",n) ;
5448      recur_flg = FALSE ; EXRETURN ;
5449    }
5450 
5451    /** If no recursion is now occurring, mark for possible recursion later.
5452        This assumes that each level of recursion does not spawn new levels
5453        yet again via the send_CB callback.  If this were possible, the
5454        code for recursion prevention would need to be more complicated! **/
5455 
5456    if( ! recur_flg ){ recur_flg = TRUE ; recur_n = n ; recur_seq = seq ; }
5457 
5458    /** find the image that is being seen right now **/
5459 
5460    nrold = seq->im_nr ;
5461    seq->im_label[0] = '\0' ;  /* forces redraw of text */
5462 
5463    /** set the image number to be displayed now **/
5464 
5465    if( n >= 0 && !ISQ_set_image_number(seq,n) ){
5466      if( RECUR ) recur_flg = FALSE ; EXRETURN ;
5467    }
5468 
5469    MCW_discard_events_all( seq->wimage , ButtonPressMask ) ;  /* 20 Mar 2007 */
5470 
5471    switch( type ){
5472       default: { if( RECUR ) recur_flg = FALSE ; EXRETURN ; }
5473 
5474       case isqDR_display:
5475          kill_im = kill_ov = True ;            /* do both images */
5476       break ;
5477 
5478       case isqDR_overlay:
5479          kill_im = (n >=0 ) && (n != nrold) ;  /* only do im if new */
5480          kill_ov = True ;                      /* do overlay */
5481       break ;
5482 
5483       case isqDR_reimage:
5484          kill_ov = (n >=0 ) && (n != nrold) ;
5485          kill_im = True ;
5486       break ;
5487 
5488       case isqDR_reshow:
5489          kill_ov = kill_im = (n >=0 ) && (n != nrold) ; /* only if new */
5490       break ;
5491    }
5492 
5493    if( kill_im ) KILL_1MRI( seq->imim ) ;
5494    if( kill_ov ) KILL_1MRI( seq->ovim ) ;
5495 
5496    if( kill_ov || kill_im ) KILL_2XIM( seq->given_xim , seq->sized_xim  ) ;
5497 
5498    if( kill_ov || kill_im ){
5499       MCW_kill_XImage( seq->zoom_xim ) ; seq->zoom_xim = NULL ;
5500    }
5501 
5502    seq->scl_label[0] = '\0' ;
5503    ISQ_show_image( seq ) ;
5504    ISQ_rowgraph_draw( seq ) ;
5505    ISQ_surfgraph_draw( seq ) ;  /* 21 Jan 1999 */
5506    ISQ_set_crop_hint( seq ) ;   /* 25 Aug 2009 */
5507 
5508    if( seq->graymap_mtd != NULL ) ISQ_graymap_draw( seq ) ; /* 24 Oct 2003 */
5509 
5510    /* 24 Apr 2001: handle image recording */
5511 
5512    if( RECORD_ISON(seq->record_status) && seq->zoom_fac == 1 ){
5513       int pos , meth ;
5514 
5515       /* compute where to put this sucker */
5516 
5517       switch( seq->record_method ){
5518          default:
5519          case RECORD_METHOD_AFTEREND:     pos = 987654321; meth =  1; break;
5520          case RECORD_METHOD_BEFORESTART:  pos =  0       ; meth = -1; break;
5521          case RECORD_METHOD_INSERT_MM:    pos = -1       ; meth = -1; break;
5522          case RECORD_METHOD_INSERT_PP:    pos = -1       ; meth =  1; break;
5523          case RECORD_METHOD_OVERWRITE:    pos = -1       ; meth =  0; break;
5524          case RECORD_METHOD_OVERWRITE_MM: pos = -2       ; meth =  0; break;
5525          case RECORD_METHOD_OVERWRITE_PP: pos = -3       ; meth =  0; break;
5526       }
5527 
5528       /* put it there */
5529 
5530       ISQ_record_addim( seq , pos , meth ) ;
5531 
5532       /* if recording just one, switch status off */
5533 
5534       if( seq->record_status == RECORD_STATUS_NEXTONE ){
5535          seq->record_status = RECORD_STATUS_OFF ;
5536          MCW_set_bbox( seq->record_status_bbox , RECORD_STATUS_OFF ) ;
5537          MCW_invert_widget( seq->record_cbut ) ;
5538       }
5539    }
5540 
5541    /* exit stage left */
5542 
5543    if( RECUR ) recur_flg = FALSE ;
5544    EXRETURN ;
5545 }
5546 
5547 /*------------------------------------------------------------------------
5548    set image number in an imseq;
5549    return value is 0 if this can't be done, 1 if things go OK
5550 --------------------------------------------------------------------------*/
5551 
ISQ_set_image_number(MCW_imseq * seq,int n)5552 int ISQ_set_image_number( MCW_imseq *seq , int n )
5553 {
5554 ENTRY("ISQ_set_image_number") ;
5555 
5556    if( ! ISQ_VALID(seq) ) RETURN(0) ;
5557 
5558    if( n < 0 || n >= seq->status->num_total ){
5559 
5560      if( seq->status->num_total > 1 ){
5561        XBell( seq->dc->display , 100 ) ;
5562        fprintf(stderr,"\n*** ILLEGAL IMAGING:\n"
5563                       " ISQ_set_image_number %d\n",n);
5564 
5565        fprintf(stderr," status: num_total=%d num_series=%d\n",
5566                seq->status->num_total , seq->status->num_series ) ;
5567      } else {
5568        XmScaleSetValue( seq->wscale , 0 ) ;  /* 08 Aug 2001 */
5569      }
5570 
5571      RETURN(0) ;
5572    }
5573 
5574    if( seq->im_nr != n ){
5575      XmScaleSetValue( seq->wscale , n ) ;  /* be sure to change scale */
5576 
5577      if( seq->status->send_CB != NULL ){   /* send info to AFNI */
5578        ISQ_cbs cbs ;
5579        seq->im_nr = n ;
5580        cbs.reason = isqCR_newimage ;
5581        cbs.nim    = seq->im_nr ;
5582 #if 0
5583        seq->status->send_CB( seq , seq->getaux , &cbs ) ;
5584 #else
5585        SEND(seq,cbs) ;  /* AFNI will do the redisplay for all viewers */
5586 #endif
5587      } else {           /* no AFNI == do the redisplay now myself */
5588 #if 0
5589        ISQ_redisplay( seq , n , isqDR_display ) ;  /* 07 Nov 2002 */
5590 #endif
5591      }
5592    }
5593    RETURN(1) ;
5594 }
5595 
5596 /*-------------------------------------------------------------------------*/
5597 
5598 /* 15 Mar 2002: stuff for processing X11 errors */
5599 
5600 static volatile int xwasbad ;
5601 typedef int (*xhandler)(Display *, XErrorEvent *) ;
qhandler(Display * dpy,XErrorEvent * xev)5602 static int qhandler( Display *dpy , XErrorEvent *xev ){ xwasbad=1; return 0; }
5603 
5604 /*-----------------------------------------------------------------------*/
5605 
ISQ_center_zoom(MCW_imseq * seq)5606 void ISQ_center_zoom( MCW_imseq *seq ) /* 27 Aug 2009 */
5607 {
5608    int zlev,xcen,ycen ; int_triple xyn ; float mh,zwid , xch,ych ;
5609 
5610 ENTRY("ISQ_center_zoom") ;
5611 
5612    if( !ISQ_REALZ(seq) || seq->imim == NULL || seq->zoom_fac <= 1 ) EXRETURN ;
5613 
5614 #if 0
5615    if( seq->cropit ) /* centering on crop region not implemented */ EXRETURN ;
5616 #endif
5617 
5618    xyn = ISQ_get_crosshairs(seq) ; xcen = xyn.i ; ycen = xyn.j ;
5619    if( xcen < 0 || ycen < 0 )                                       EXRETURN ;
5620    ISQ_unflipxy( seq , &xcen , &ycen ) ; /* flip to screen coord */
5621    if( xcen < 0 || ycen < 0 )                                       EXRETURN ;
5622    xch = xcen / (float)seq->imim->nx ; if( xch >= 1.0f )            EXRETURN ;
5623    ych = ycen / (float)seq->imim->ny ; if( ych >= 1.0f )            EXRETURN ;
5624 
5625    zlev = seq->zoom_fac ;
5626    mh   = (zlev-1.001f)/zlev ; /* max offset allowed */
5627    zwid = 0.5f / zlev ;
5628 
5629    seq->zoom_hor_off = xch - zwid ;
5630    seq->zoom_ver_off = ych - zwid ;
5631         if( seq->zoom_hor_off > mh   ) seq->zoom_hor_off = mh  ;
5632    else if( seq->zoom_hor_off < 0.0f ) seq->zoom_hor_off = 0.0f ;
5633         if( seq->zoom_ver_off > mh   ) seq->zoom_ver_off = mh  ;
5634    else if( seq->zoom_ver_off < 0.0f ) seq->zoom_ver_off = 0.0f ;
5635 
5636    ISQ_redisplay( seq , -1 , isqDR_display ) ;
5637 
5638    EXRETURN ;
5639 }
5640 
5641 /*-----------------------------------------------------------------------*/
5642 /* Put the zoomed image fragment to the display */
5643 
ISQ_show_zoom(MCW_imseq * seq)5644 int ISQ_show_zoom( MCW_imseq *seq )   /* 11 Mar 2002 */
5645 {
5646    int iw,ih , zlev=seq->zoom_fac , pw,ph , xoff,yoff , newim=0 , flash=0 ;
5647    static int busy=0 ;                /* 23 Jan 2004 */
5648 
5649 ENTRY("ISQ_show_zoom") ;
5650 
5651    if( busy ){ STATUS(" recursive entry!"); RETURN(-1); }          /* recursion = bad */
5652    busy = 1 ;
5653 
5654    /* find the size of the image window */
5655 
5656    MCW_widget_geom( seq->wimage, &iw,&ih , NULL,NULL ) ;
5657 
5658    /* pixmap should be size of image window, scaled up;
5659       if it isn't that size already, free it right now */
5660 
5661    pw = iw*zlev ; ph = ih*zlev ;
5662 
5663    if( seq->zoom_pixmap != (Pixmap) 0 &&
5664        (pw != seq->zoom_pw || ph != seq->zoom_ph) ){
5665 
5666 STATUS("freeing old pixmap") ;
5667       XFreePixmap( seq->dc->display , seq->zoom_pixmap ) ;
5668       seq->zoom_pixmap = (Pixmap) 0 ;
5669       newim++ ;
5670    }
5671 
5672    /* (re)make the pixmap, if needed;
5673       it will be saved in the seq struct for next time */
5674 
5675    if( seq->zoom_pixmap == (Pixmap) 0 ){
5676       xhandler old_handler = XSetErrorHandler(qhandler); xwasbad = 0;
5677 
5678 STATUS("creating new pixmap") ;
5679       seq->zoom_pixmap = XCreatePixmap( seq->dc->display ,
5680                                         XtWindow(seq->wimage) ,
5681                                         pw , ph , seq->dc->depth ) ;
5682 
5683       (void) XSetErrorHandler(old_handler) ;
5684 
5685       /* if allocating pixmap failed, exit now */
5686 
5687       if( xwasbad ){
5688         fprintf(stderr,"** Can't zoom - out of memory! **\n\a");
5689         AV_assign_ival( seq->zoom_val_av , ZOOM_BOT ) ;
5690         ISQ_zoom_av_CB( seq->zoom_val_av , seq ) ;
5691         busy = 0 ; RETURN(-1) ;
5692       }
5693 
5694       seq->zoom_pw = pw ; seq->zoom_ph = ph ;
5695       newim++ ;
5696    }
5697 
5698    /* if we made a new pixmap, we'll need a new zoomed image for it */
5699 
5700    if( newim && seq->zoom_xim != NULL ){
5701 STATUS("killing old XImage because have new image") ;
5702      MCW_kill_XImage( seq->zoom_xim ) ; seq->zoom_xim = NULL ;
5703    }
5704 
5705    /* scale up the XImage given_xim, if needed;
5706       it will be save in the seq struct for next time,
5707       unless the image changes, in which case it will have been axed */
5708 
5709    if( seq->zoom_xim == NULL ){  /* zoom_xim will be the thing we pan around */
5710      MRI_IMAGE *im , *tim ;
5711 STATUS("inverting zoom label") ;
5712      flash = 1 ; MCW_invert_widget( seq->zoom_val_av->wlabel ) ;
5713 STATUS("converting given XImage to MRI_IMAGE") ;
5714      im  = XImage_to_mri( seq->dc, seq->given_xim, X2M_USE_CMAP|X2M_FORCE_RGB ) ;
5715 STATUS("zooming up MRI_IMAGE") ;
5716      tim = mri_dup2D(zlev,im) ; mri_free(im) ;
5717 STATUS("converting zoomed MRI_IMAGE back to XImage") ;
5718      seq->zoom_xim = mri_to_XImage(seq->dc,tim) ; mri_free(tim) ;
5719      newim++ ;
5720    }
5721 
5722    /* if zoomed image isn't same size as pixmap, resize it here */
5723 
5724    if( pw != seq->zoom_xim->width || ph != seq->zoom_xim->height ){
5725      XImage *sxim ;
5726      sxim = resize_XImage( seq->dc , seq->zoom_xim , pw , ph ) ;
5727 STATUS("killing old XImage because doesn't fit pixmap") ;
5728      MCW_kill_XImage( seq->zoom_xim ) ;
5729      seq->zoom_xim = sxim ;
5730      newim++ ;
5731    }
5732 
5733    /* if have a new image, put the zoomed XImage into the Pixmap */
5734 
5735    if( newim ){
5736 STATUS("putting new image into pixmap") ;
5737      XPutImage( seq->dc->display ,
5738                 seq->zoom_pixmap ,
5739                 seq->dc->origGC  , seq->zoom_xim , 0,0,0,0 , pw,ph ) ;
5740 
5741      /* draw the overlay graph into the Pixmap */
5742 
5743      if( !seq->opt.no_overlay && seq->mplot != NULL ){
5744 STATUS("drawing overlay plot into pixmap") ;
5745         memplot_to_X11_sef( seq->dc->display ,
5746                             seq->zoom_pixmap , seq->mplot ,
5747                             0,0,MEMPLOT_FREE_ASPECT        ) ;
5748      }
5749    }
5750 
5751    /* now we can copy the relevant area
5752       from the pixmap into the image window, which is very fast and smooth  */
5753 
5754    xoff = seq->zoom_hor_off * pw ; if( xoff+iw > pw ) xoff = pw-iw ;
5755    yoff = seq->zoom_ver_off * ph ; if( yoff+ih > ph ) yoff = ph-ih ;
5756 
5757 STATUS("copying from pixmap to image window") ;
5758    XCopyArea( seq->dc->display ,
5759               seq->zoom_pixmap ,
5760               XtWindow(seq->wimage) , seq->dc->origGC ,
5761               xoff , yoff , iw,ih , 0,0 ) ;
5762 
5763    if( flash ) MCW_invert_widget( seq->zoom_val_av->wlabel ) ;
5764 
5765 #if defined(DISCARD_EXCESS_EXPOSES)
5766 STATUS("discarding excess Expose events") ;
5767     MCW_discard_events( seq->wimage , ExposureMask ) ;
5768 #endif
5769 
5770    busy = 0 ; RETURN(1) ;
5771 }
5772 
5773 /*-----------------------------------------------------------------------
5774   actually put the image into window
5775   23 Apr 2001 - modified to deal with case of NULL image from
5776                 ISQ_make_image() - by drawing a string
5777 -------------------------------------------------------------------------*/
5778 
ISQ_show_image(MCW_imseq * seq)5779 void ISQ_show_image( MCW_imseq *seq )
5780 {
5781    if( seq == NULL || seq->ignore_redraws ) return ;  /* 16 Aug 2002 */
5782 ENTRY("ISQ_show_image") ;
5783 
5784    if( ! ISQ_REALZ(seq) ) EXRETURN ;
5785 
5786    if( seq->given_xbar == NULL ) ISQ_show_bar( seq ) ;  /* 22 Aug 1998 */
5787 
5788    if( seq->given_xim == NULL )  ISQ_make_image( seq ) ;
5789 
5790    if( seq->given_xim == NULL )  STATUS("bad news: given_xim == NULL!") ;
5791 
5792    if( ! MCW_widget_visible(seq->wimage) ) EXRETURN ;  /* 03 Jan 1999 */
5793 
5794    if( seq->given_xim != NULL &&
5795        seq->zoom_fac  >  1    &&
5796        seq->mont_nx   == 1    &&
5797        seq->mont_ny   == 1      ){    /* show a zoomed image instead */
5798 
5799       int ss = ISQ_show_zoom( seq ) ;  /* ss > 0 is good zoom */
5800       if( ss > 0 ){        /* if failed, fall through to code farther below */
5801         EXRETURN ;
5802       }
5803    }
5804 
5805    if( seq->given_xim != NULL && seq->sized_xim == NULL ){
5806       int nx , ny ;
5807 
5808       STATUS("making sized_xim");
5809 
5810       MCW_widget_geom( seq->wimage , &nx , &ny , NULL,NULL ) ;
5811 
5812       seq->sized_xim = resize_XImage( seq->dc , seq->given_xim , nx , ny ) ;
5813    }
5814 
5815    if( seq->sized_xim != NULL ){
5816 STATUS("putting sized_xim to screen");
5817 
5818 #if 0
5819 if( AFNI_yesenv("AFNI_IMSEQ_DEBUG") ){
5820   fprintf(stderr,"==== imseq->wimage: XPutImage w=%d h=%d\n",
5821   seq->sized_xim->width , seq->sized_xim->height ) ;
5822   DBG_traceback() ;
5823 }
5824 #endif
5825 
5826      /**** actually put the image to the screen ****/
5827 
5828 #if 0
5829 INFO_message("ISQ_show_image(seq=%p) %d x %d",
5830              (void *)seq ,
5831              (int)seq->sized_xim->width , (int)seq->sized_xim->height ) ;
5832 #endif
5833 
5834      XPutImage( seq->dc->display , XtWindow(seq->wimage) , seq->dc->origGC ,
5835                 seq->sized_xim , 0,0,0,0,
5836                 seq->sized_xim->width , seq->sized_xim->height ) ;
5837 
5838    } else {  /* 23 Apr 2001 - draw 'EMPTY IMAGE' */
5839 
5840       static MEM_plotdata *empt=NULL ;  /* only create once */
5841 
5842       if( empt == NULL ){
5843          STATUS("create EMPTY IMAGE plot") ;
5844          create_memplot_surely("EmptyImagePlot",1.0) ;
5845          /** AFNI_REPLACE_XDRAWLINES ; **/
5846          empt = get_active_memplot() ;
5847          set_color_memplot(1.0,1.0,1.0) ;
5848          set_thick_memplot(0.009) ;
5849          plotpak_pwritf( 0.4,0.83 , "EMPTY" , 96 , 0 , 0 ) ;
5850          plotpak_pwritf( 0.4,0.67 , "IMAGE" , 96 , 0 , 0 ) ;
5851          set_color_memplot(0.0,0.0,0.0) ;
5852          plotpak_pwritf( 0.6,0.33 , "EMPTY" , 96 , 0 , 0 ) ;
5853          plotpak_pwritf( 0.6,0.17 , "IMAGE" , 96 , 0 , 0 ) ;
5854          set_color_memplot(1.0,1.0,0.0) ;
5855          set_thick_memplot(0.019) ;
5856          plotpak_line( 0.01,0.01 , 0.99,0.01 ) ;
5857          plotpak_line( 0.99,0.01 , 0.99,0.99 ) ;
5858          plotpak_line( 0.99,0.99 , 0.01,0.99 ) ;
5859          plotpak_line( 0.01,0.99 , 0.01,0.01 ) ;
5860          set_thick_memplot(0.0) ;
5861          /** AFNI_RESTORE_XDRAWLINES ; **/
5862       }
5863       STATUS("display EMPTY IMAGE plot") ;
5864       XClearWindow( seq->dc->display , XtWindow(seq->wimage) ) ;
5865       memplot_to_X11_sef( seq->dc->display ,
5866                           XtWindow(seq->wimage) , empt ,
5867                           0,0,MEMPLOT_FREE_ASPECT     ) ;
5868    }
5869 
5870    /*-- 26 Feb 2001: draw some line overlay, a la coxplot? --*/
5871    /*-- 19 Sep 2001: modified to use memplot stored in seq --*/
5872 
5873    if( !seq->opt.no_overlay && seq->mplot != NULL )
5874       memplot_to_X11_sef( seq->dc->display ,
5875                           XtWindow(seq->wimage) , seq->mplot ,
5876                           0,0,MEMPLOT_FREE_ASPECT             ) ;
5877 
5878    seq->never_drawn = 0 ;
5879 
5880    ISQ_draw_winfo( seq ) ;
5881 
5882 #ifdef DISCARD_EXCESS_EXPOSES
5883     MCW_discard_events( seq->wimage , ExposureMask ) ;
5884 #endif
5885 
5886    EXRETURN ;
5887 }
5888 
5889 /*-------------------------------------------------------------------
5890   Draw the message data in the winfo label (below the image)
5891 ---------------------------------------------------------------------*/
5892 
ISQ_draw_winfo(MCW_imseq * seq)5893 void ISQ_draw_winfo( MCW_imseq *seq )
5894 {
5895    char buf[128] = "\0" ;
5896    int nn , ibuf ;
5897    ISQ_indiv_statistics *st ;
5898 
5899 ENTRY("ISQ_draw_winfo") ;
5900 
5901    if( ! ISQ_REALZ(seq) ) EXRETURN ;
5902 
5903    if( seq->last_image_type >= 0 ){
5904      sprintf( buf , "%s" , MRI_TYPE_name[seq->last_image_type] ) ;
5905 
5906      if( seq->last_image_type == MRI_complex ){
5907        switch( seq->opt.cx_code ){
5908          case ISQ_CX_MAG:   strcat( buf , "[mag]" ) ; break ;
5909          case ISQ_CX_PHASE: strcat( buf , "[arg]" ) ; break ;
5910          case ISQ_CX_REAL:  strcat( buf , "[re]"  ) ; break ;
5911          case ISQ_CX_IMAG:  strcat( buf , "[im]"  ) ; break ;
5912          default: break ;
5913        }
5914      }
5915    }
5916    ibuf = strlen(buf) ;
5917 
5918    nn = seq->im_nr ;  if( nn < 0 ) EXRETURN ;
5919 
5920    st = &( seq->imstat[nn] ) ;
5921 #if 0
5922    if( st->one_done ){
5923 #if 0
5924       if( seq->opt.scale_group == ISQ_SCL_AUTO   &&
5925           seq->opt.scale_range == ISQ_RNG_02TO98    )
5926 
5927            sprintf( buf+ibuf , " 2%%=%g 98%%=%g", st->per02 , st->per98 ) ;
5928       else
5929 #endif
5930 
5931 #if 0
5932            sprintf( buf+ibuf , "=%g..%g ent=%.2f" ,
5933                     st->min , st->max , st->entropy ) ;
5934 #endif
5935            sprintf( buf+ibuf , "=%g..%g" , st->min , st->max ) ;
5936    }
5937 #endif
5938 
5939    if( seq->scl_label[0] != '\0' )
5940      sprintf(buf+strlen(buf)," %s",seq->scl_label) ;
5941    if( (seq->opt.improc_code & ISQ_IMPROC_SHARP) )
5942      sprintf(buf+strlen(buf)," s=%d",(int)(10.0*seq->sharp_fac+.01)) ;
5943    if( seq->render_mode ) strcat(buf,"#") ;
5944 
5945    if( seq->im_label[0] == '\0' || strcmp(buf,seq->im_label) != 0 ){
5946      char qbuf[128] ; qbuf[0] = '\0' ;
5947      if( seq->winfo_prefix[0] != '\0' ){  /* 10 Dec 2007 */
5948        strcat(qbuf,seq->winfo_prefix) ; strcat(qbuf,": ") ;
5949      }
5950      if( seq->winfo_extra[0] == '\0' ){
5951 
5952        int iw=0 ;                                   /* winfo_sides stuff */
5953        switch( seq->opt.rot ){                      /* from 01 Dec 1999  */
5954          case ISQ_ROT_0  : iw=0 ; break ;
5955          case ISQ_ROT_90 : iw=1 ; break ;
5956          case ISQ_ROT_180: iw=2 ; break ;
5957          case ISQ_ROT_270: iw=3 ; break ;
5958          default: break ;
5959        }
5960        if( seq->opt.mirror ) iw = (iw+2)%4 ;
5961 
5962        if( seq->winfo_sides[iw][0] != '\0' ){
5963          strcat(qbuf,"left=") ;
5964          strcat(qbuf,seq->winfo_sides[iw]) ;
5965          strcat(qbuf," ") ; strcat(qbuf,buf) ;
5966          MCW_set_widget_label( seq->winfo , qbuf ) ;
5967        } else if( seq->opt.mirror || seq->opt.rot != ISQ_ROT_0 ){
5968          switch( seq->opt.rot ){
5969            case ISQ_ROT_0  : strcat(qbuf,"["   ) ; break ;
5970            case ISQ_ROT_90 : strcat(qbuf,"[90" ) ; break ;
5971            case ISQ_ROT_180: strcat(qbuf,"[180") ; break ;
5972            case ISQ_ROT_270: strcat(qbuf,"[270") ; break ;
5973            default: break ;
5974          }
5975          if( seq->opt.mirror ){
5976            if( seq->opt.rot == ISQ_ROT_0 ) strcat(qbuf,"l] " ) ;
5977            else                            strcat(qbuf,"+l] ") ;
5978          } else                            strcat(qbuf,"] "  ) ;
5979          strcat(qbuf,buf) ;
5980          MCW_set_widget_label( seq->winfo , qbuf ) ;
5981        } else {
5982          MCW_set_widget_label( seq->winfo , buf ) ;   /* default label! */
5983        }
5984 
5985      } else {                                        /* winfo_extra stuff */
5986        strcpy(qbuf,seq->winfo_extra) ;              /* from 07 Aug 1999  */
5987        strcat(qbuf," ") ; strcat(qbuf,buf) ;
5988        MCW_set_widget_label( seq->winfo , qbuf ) ;
5989      }
5990      strcpy(seq->im_label,buf) ;
5991    }
5992 
5993    MCW_set_widget_label( seq->rinfo , seq->rinfo_label ) ;
5994    EXRETURN ;
5995 }
5996 
5997 /*-----------------------------------------------------------------------
5998   Put a range hint on the color bar, if possible -- 29 Jul 2001
5999 -------------------------------------------------------------------------*/
6000 
ISQ_set_barhint(MCW_imseq * seq,char * lab)6001 void ISQ_set_barhint( MCW_imseq *seq , char *lab )
6002 {
6003    char sbot[16],stop[16] , hint[64] , *sb,*st ;
6004 
6005 ENTRY("ISQ_set_barhint") ;
6006 
6007    if( !ISQ_REALZ(seq) ) EXRETURN ;            /* bad news */
6008 
6009    floatfix(seq->barbot) ; floatfix(seq->bartop) ; /* 24 Aug 2009 */
6010 
6011    if( seq->barbot < seq->bartop ){            /* can make a hint */
6012       AV_fval_to_char( seq->barbot , sbot ) ;  /* convert to nice strings */
6013       AV_fval_to_char( seq->bartop , stop ) ;
6014       sb = (sbot[0] == ' ') ? sbot+1 : sbot ;  /* skip leading blanks */
6015       st = (stop[0] == ' ') ? stop+1 : stop ;
6016       if( lab != NULL && strlen(lab) < 32 )    /* create hint */
6017          sprintf(hint,"%s: %s .. %s",lab,sb,st) ;
6018       else
6019          sprintf(hint,"%s .. %s",sb,st) ;
6020       MCW_register_hint( seq->wbar , hint ) ;  /* send to hint system */
6021    } else {
6022       MCW_unregister_hint( seq->wbar ) ;       /* don't have a hint */
6023    }
6024 
6025    EXRETURN ;
6026 }
6027 
6028 /*-------------------------------------------------------------------*/
6029 
ISQ_set_cursor_state(MCW_imseq * seq,int cstat)6030 void ISQ_set_cursor_state( MCW_imseq *seq , int cstat )  /* 10 Mar 2003 */
6031 {
6032    if( seq->zoom_button1 || seq->record_mode ){
6033      /* XBell(seq->dc->display,100); */ return;
6034    }
6035 
6036 #if 0
6037 fprintf(stderr,"ISQ_set_cursor_state: old=%d new=%d\n",seq->cursor_state,cstat);
6038 #endif
6039 
6040    switch( cstat ){
6041      default:
6042        POPUP_cursorize( seq->wimage ) ;
6043        seq->cursor_state = CURSOR_NORMAL ;
6044        MCW_set_bbox( seq->pen_bbox , 0 ) ;
6045      break ;
6046 
6047      case CURSOR_PENCIL:
6048        PENCIL_cursorize( seq->wimage ) ;
6049        seq->cursor_state = CURSOR_PENCIL ;
6050        MCW_set_bbox( seq->pen_bbox , 1 ) ;
6051      break ;
6052 
6053      case CURSOR_CROSSHAIR:
6054        CROSSHAIR_cursorize( seq->wimage ) ;
6055        seq->cursor_state = CURSOR_CROSSHAIR ;
6056        MCW_set_bbox( seq->pen_bbox , 0 ) ;
6057      break ;
6058    }
6059    return ;
6060 }
6061 
6062 /*-------------------------------------------------------------------
6063   actually put the color bar into its window
6064 ---------------------------------------------------------------------*/
6065 
ISQ_show_bar(MCW_imseq * seq)6066 void ISQ_show_bar( MCW_imseq *seq )
6067 {
6068    if( seq == NULL || seq->ignore_redraws ) return ;  /* 16 Aug 2002 */
6069 ENTRY("ISQ_show_bar") ;
6070 
6071    if( ! ISQ_REALZ(seq) ) EXRETURN ;
6072 
6073    if( ! MCW_widget_visible(seq->wbar) ) EXRETURN ;  /* 03 Jan 1999 */
6074 
6075    if( seq->given_xbar == NULL ) ISQ_make_bar( seq ) ;
6076 
6077    if( seq->sized_xbar == NULL ){
6078       int nx , ny ; char *eee ;
6079 STATUS("making sized_xbar");
6080 
6081       MCW_widget_geom( seq->wbar , &nx , &ny , NULL,NULL ) ;
6082 
6083       seq->sized_xbar = resize_XImage( seq->dc, seq->given_xbar, nx, ny ) ;
6084 
6085       eee = getenv("AFNI_PBAR_TICK") ;
6086       if( eee == NULL || ( toupper(*eee) != 'N' && *eee != '0' ) ){
6087         int jj,kk ;
6088         for( kk=1 ; kk <= 9 ; kk++ ){      /* tic marks [25 Jun 2013] */
6089           jj = (int)rintf( 0.1f*kk*ny ) ;
6090           rectzero_XImage( seq->dc , seq->sized_xbar , 0   ,jj , 2   ,jj ) ;
6091           rectzero_XImage( seq->dc , seq->sized_xbar , nx-3,jj , nx-1,jj ) ;
6092         }
6093       }
6094    }
6095 
6096    if( seq->sized_xbar != NULL ){
6097 STATUS("putting sized_xbar to screen");
6098 
6099      XPutImage( seq->dc->display , XtWindow(seq->wbar) , seq->dc->origGC ,
6100                 seq->sized_xbar , 0,0,0,0,
6101                 seq->sized_xbar->width , seq->sized_xbar->height ) ;
6102    }
6103 
6104 #ifdef DISCARD_EXCESS_EXPOSES
6105     MCW_discard_events( seq->wbar , ExposureMask ) ;
6106 #endif
6107 
6108    EXRETURN ;
6109 }
6110 
6111 /*-----------------------------------------------------------------------
6112    Handle all events in an imseq drawing area widget (image or bar).
6113    Feb 1998: Button2 events are passed to their own handler.
6114 -------------------------------------------------------------------------*/
6115 
ISQ_drawing_EV(Widget w,XtPointer client_data,XEvent * ev,RwcBoolean * continue_to_dispatch)6116 void ISQ_drawing_EV( Widget w , XtPointer client_data ,
6117                      XEvent *ev , RwcBoolean *continue_to_dispatch )
6118 {
6119    MCW_imseq *seq = (MCW_imseq *) client_data ;
6120    static ISQ_cbs cbs ;
6121    static int busy=0 ;   /* 23 Jan 2004: prevent recursion */
6122 
6123    static int doing_icor=0 ; /* 27 Sep 2021 */
6124 
6125 ENTRY("ISQ_drawing_EV") ;
6126 
6127    if( busy ){ STATUS("recursive entry!"); EXRETURN; }  /* bad! */
6128    if( !ISQ_REALZ(seq) ) EXRETURN ;
6129    busy = 1 ;
6130 
6131    if(PRINT_TRACING){
6132      char str[256], *wn ;
6133           if( w == seq->wimage ) wn = "wimage" ;
6134      else if ( w == seq->wbar  ) wn = "wbar"   ;
6135      else                        wn = XtName(w) ;
6136      sprintf(str,"Widget=%s Event type=%d",wn,ev->type);
6137      STATUS(str) ;
6138    }
6139 
6140    /** memset(&cbs,0,sizeof(ISQ_cbs)) ; **/
6141 
6142    switch( ev->type ){
6143 
6144       /*----- button release event -----*/
6145 
6146       case ButtonRelease:{
6147          XButtonEvent *event = (XButtonEvent *) ev ;
6148          int but = event->button ;
6149 
6150          /** 03 Oct 2002: change Shift+Button1 into Button2, then send to that event handler **/
6151 
6152          if( but == Button1 &&
6153              ( seq->cursor_state == CURSOR_PENCIL ||
6154                ((event->state & ShiftMask) && !(event->state & ControlMask)) ) ){
6155            event->button = but = Button2 ;
6156            if( seq->button2_enabled && w == seq->wimage )
6157               ISQ_button2_EV( w , client_data , ev , continue_to_dispatch ) ;
6158            else
6159               { /* XBell(seq->dc->display,100); */ busy=0;EXRETURN; }
6160          }
6161 
6162          /* Button1 release: turn off zoom-pan mode, if it was on */
6163 
6164          if( event->button == Button1 && w == seq->wimage ){
6165            int xrel=event->x , yrel=event->y ; int scd=seq->shft_ctrl_dragged ;
6166 
6167            seq->shft_ctrl_dragged = 0 ;  /* 17 Mar 2010 */
6168 
6169            if( seq->zoom_button1 && !AFNI_yesenv("AFNI_KEEP_PANNING") ){
6170              seq->zoom_button1 = 0 ;
6171              POPUP_cursorize( seq->wimage ) ;
6172              MCW_invert_widget( seq->zoom_drag_pb ) ;
6173            } else if( !seq->zoom_button1 ){              /* 23 Oct 2003 */
6174              if( seq->cmap_changed ){
6175                COLORMAP_CHANGE(seq); seq->cmap_changed = 0;
6176                if( seq->graymap_mtd != NULL && AFNI_yesenv("AFNI_STROKE_AUTOPLOT") ){
6177                  NI_sleep(666) ;     /* pop down after a short delay */
6178                  plotkill_topshell( seq->graymap_mtd ) ;
6179                  seq->graymap_mtd = NULL ;
6180                }
6181              } else if( seq->status->send_CB != NULL ){  /* 04 Nov 2003 */
6182                 int imx,imy,nim;
6183                 seq->wimage_width = -1 ;
6184                 if( scd || abs(seq->last_bx-xrel)+abs(seq->last_by-yrel) < 8 ){
6185                   int xuse,yuse ;
6186                   if( scd ){ xuse = xrel        ; yuse = yrel        ; }
6187                   else     { xuse = seq->last_bx; yuse = seq->last_by; }
6188                   ISQ_mapxy( seq , xuse,yuse , &imx,&imy,&nim ) ;
6189                   cbs.reason = isqCR_buttonpress ;
6190                   cbs.event  = ev ;
6191                   cbs.xim    = imx ;       /* delayed send of Button1 */
6192                   cbs.yim    = imy ;       /* event to AFNI now       */
6193                   cbs.nim    = nim ;
6194 
6195                   if( doing_icor ){             /* 27 Sep 2021 */
6196                     event->state &= ~ShiftMask ;
6197                     event->state &= ~ControlMask ;
6198                     doing_icor    = 0 ;
6199                   }
6200 #if 0
6201                   seq->status->send_CB( seq , seq->getaux , &cbs ) ;
6202 #else
6203                   SEND(seq,cbs) ;
6204 #endif
6205                }
6206              }
6207            }
6208          }
6209       }
6210       break ;  /*--- end of ButtonRelease ---*/
6211 
6212       /*----- motion with Button #1 pressed down -----*/
6213 
6214       case MotionNotify:{
6215         XMotionEvent *event = (XMotionEvent *) ev ;
6216         int bx,by ;
6217 
6218         /** 03 Oct 2002: change Shift+Button1 into Button2, send to event handler **/
6219 
6220         if( (event->state & Button1Mask) &&
6221              ( seq->cursor_state == CURSOR_PENCIL ||
6222                ((event->state & ShiftMask) && !(event->state & ControlMask)) ) ){
6223           event->state |= Button2Mask ;
6224           if( seq->button2_enabled && w == seq->wimage )
6225              ISQ_button2_EV( w , client_data , ev , continue_to_dispatch ) ;
6226           else
6227              { /* XBell(seq->dc->display,100); */ busy=0;EXRETURN; }
6228           busy=0;EXRETURN ;
6229         }
6230 
6231         /** 17 Mar 2010: Shift+Ctrl+Button1 = send to our master [InstaCorr] **/
6232 
6233         if( (event->state & Button1Mask) && (seq->status->send_CB != NULL) &&
6234             (event->state & ShiftMask)   && (event->state & ControlMask)     ){
6235           int imx,imy,nim;
6236           seq->wimage_width = -1 ;
6237           ISQ_mapxy( seq , event->x,event->y , &imx,&imy,&nim ) ;
6238           cbs.reason = isqCR_buttonmove ;
6239           cbs.event  = ev ;
6240           cbs.xim    = imx ;       /* delayed send of Button1 */
6241           cbs.yim    = imy ;       /* event to AFNI now       */
6242           cbs.nim    = nim ;
6243           doing_icor = 1 ;
6244           SEND(seq,cbs) ; busy=0 ; seq->shft_ctrl_dragged=1 ; EXRETURN ;
6245         }
6246 
6247         /* Button1 motion: if not panning, changing the color/gray map? */
6248 
6249         if( !seq->zoom_button1 && (event->state & Button1Mask) ){
6250           int xdif = (event->x - seq->last_bx) ;
6251           int ydif = (event->y - seq->last_by) ;
6252           if( !seq->dc->use_xcol_im && (xdif || ydif) ){
6253             double denom = AFNI_numenv("AFNI_STROKE_THRESHOLD") ;
6254             if( denom < 1.0l ){
6255               if( getenv("AFNI_STROKE_THRESHOLD") != NULL ){ busy=0;EXRETURN ;}
6256               denom = 32.0l ;
6257             }
6258             xdif = rint(xdif/denom) ; ydif = rint(ydif/denom) ;
6259             if( xdif || ydif ){                             /* if big enough change */
6260               if( seq->imim != NULL && seq->imim->kind == MRI_rgb ){ /* 26 Apr 2005 */
6261 
6262                      if( xdif > 0 ){ seq->rgb_gamma  *= 0.95 ; } /* change the RGB */
6263                 else if( xdif < 0 ){ seq->rgb_gamma  /= 0.95 ; }     /* colorizing */
6264                      if( ydif < 0 ){ seq->rgb_offset += 0.014; }
6265                 else if( ydif > 0 ){ seq->rgb_offset -= 0.014; }
6266                 ISQ_redisplay( seq , -1 , isqDR_reimage ) ;
6267                 seq->cmap_changed = 1 ;
6268                 seq->last_bx = event->x ; seq->last_by = event->y;
6269 
6270               } else {                          /* the old way: change the gray map */
6271 
6272                 if( xdif ){ DC_gray_conbrio(seq->dc, xdif); seq->last_bx=event->x;}
6273                 if( ydif ){ DC_gray_change (seq->dc,-ydif); seq->last_by=event->y;}
6274                 seq->cmap_changed = 1 ;
6275                 if( seq->dc->visual_class == TrueColor ){
6276                   if( seq->graymap_mtd == NULL &&
6277                       AFNI_yesenv("AFNI_STROKE_AUTOPLOT") ) ISQ_graymap_draw( seq ) ;
6278                   KILL_2XIM( seq->given_xbar , seq->sized_xbar ) ;
6279                   ISQ_redisplay( seq , -1 , isqDR_display ) ;
6280                 } else {
6281                   if( seq->graymap_mtd != NULL ) ISQ_graymap_draw( seq ) ;
6282                 }
6283               }
6284             }
6285           }
6286           busy=0; EXRETURN ;
6287         }  /* end of altering colormap */
6288 
6289         /* Button1 motion: check for being in zoom-pan mode */
6290 
6291         if( !seq->zoom_button1              ||
6292             seq->zoom_fac == 1              ||
6293             seq->zoom_xim == NULL           ||
6294             (event->state & Button1Mask)==0   ){ busy=0; EXRETURN; } /* not zoom-pan? */
6295 
6296         /*-- if here, change panning offset --*/
6297 
6298         bx = event->x ; by = event->y ;
6299         ISQ_actually_pan( seq , (bx>seq->zoom_xp) ? -1
6300                                :(bx<seq->zoom_xp) ?  1 : 0 ,
6301                                 (by>seq->zoom_yp) ? -1
6302                                :(by<seq->zoom_yp) ?  1 : 0   ) ;
6303 
6304         seq->zoom_xp = bx ; seq->zoom_yp = by ;
6305 
6306         busy=0; EXRETURN ;
6307       }
6308       break ;  /*--- end of MotionNotify ---*/
6309 
6310       /*----- redraw -----*/
6311 
6312       case Expose:{
6313          XExposeEvent *event = (XExposeEvent *) ev ;
6314 
6315 DPRI(" .. Expose; count=",event->count) ;
6316 
6317          XSync( XtDisplay(w) , False ) ;
6318          if( event->count == 0 ){      /* don't bother if more Expose to come */
6319             if( w == seq->wimage ){    /* 25 Sep 2000: check for hidden resizes */
6320                int nx,ny ;
6321                MCW_widget_geom( seq->wimage , &nx , &ny , NULL,NULL ) ;
6322 
6323                if( seq->sized_xim != NULL &&
6324                    ( (nx != seq->sized_xim->width ) ||
6325                      (ny != seq->sized_xim->height)   ) ){  /* found a hidden resize */
6326                                                             /* so let's un-hide it! */
6327                   XConfigureEvent nev ;
6328 
6329 STATUS(" .. really a hidden resize") ;
6330 
6331 #if 0
6332 INFO_message("convert Expose to ConfigureNotify") ;
6333 #endif
6334                   nev.type = ConfigureNotify ; nev.width = nx ; nev.height = ny ;
6335                   ISQ_drawing_EV( w, client_data, (XEvent *) &nev, continue_to_dispatch ) ;
6336 
6337                } else
6338 #if 0
6339 INFO_message("Expose") ;
6340 #endif
6341                   ISQ_show_image( seq ) ;
6342             }
6343             else if( w == seq->wbar )
6344                ISQ_show_bar( seq ) ;
6345 
6346 #ifdef DISCARD_EXCESS_EXPOSES
6347             STATUS("discarding excess Expose events") ;
6348             MCW_discard_events( w , ExposureMask ) ;
6349 #endif
6350          }
6351       }
6352       break ;  /*--- end of Expose ---*/
6353 
6354       /*----- take key press -----*/
6355 
6356       case KeyPress:{
6357          XKeyEvent *event = (XKeyEvent *) ev ;
6358          char       buf[32] ;
6359          int        nbuf ;
6360          KeySym     ks ;
6361 
6362 STATUS(" .. KeyPress") ;
6363 
6364          ISQ_timer_stop(seq) ;  /* 03 Dec 2003 */
6365          doing_icor = 0 ;
6366 
6367          /* discard if a mouse button is also pressed at this time */
6368 
6369          if( event->state & (Button1Mask|Button2Mask|Button3Mask) ){
6370            /* XBell(seq->dc->display,100); */ busy=0; EXRETURN;
6371          }
6372 
6373          /* get the string corresponding to the key pressed */
6374 
6375          buf[0] = '\0' ;
6376          ks     = 0 ;
6377          nbuf = XLookupString( event , buf , 32 , &ks , NULL ) ;
6378 #if 0
6379 fprintf(stderr,"KeySym=%04x nbuf=%d state=%u\n",(unsigned int)ks,nbuf,event->state) ;
6380 #endif
6381 
6382          /* 24 Jan 2003: deal with special function keys */
6383 
6384          if( nbuf == 0 || ks > 255 ){
6385            if( seq->record_mode ){ busy=0; EXRETURN ; }
6386            nbuf = ISQ_handle_keypress( seq , (unsigned long)ks ,
6387                                              (unsigned int )event->state ) ;
6388            busy=0; EXRETURN ;
6389          }
6390 
6391          nbuf = ISQ_handle_keypress( seq , (unsigned long)buf[0] , 0 ) ;
6392          if( nbuf ){ busy=0; EXRETURN; }
6393 
6394          /* in special modes (record, Button2, zoom-pan) mode, this is bad */
6395 
6396          if( seq->record_mode || seq->button2_active || seq->zoom_button1 ){
6397            /* XBell(seq->dc->display,100); */ busy=0; EXRETURN;
6398          }
6399 
6400          /* un-handled as yet? notify the master, if we have one */
6401 
6402          if( w == seq->wimage && seq->status->send_CB != NULL ){
6403            cbs.reason = isqCR_keypress ;
6404            cbs.event  = ev ;
6405            cbs.key    = buf[0] ;
6406            cbs.nim    = seq->im_nr ;
6407 #if 0
6408            seq->status->send_CB( seq , seq->getaux , &cbs ) ;
6409 #else
6410            SEND(seq,cbs) ;
6411 #endif
6412          }
6413       }
6414       break ;  /*--- end of KeyPress ---*/
6415 
6416       /*----- take button press -----*/
6417 
6418       case ButtonPress:{
6419          XButtonEvent *event = (XButtonEvent *) ev ;
6420          int bx,by , width,height , but ;
6421 
6422 STATUS(" .. ButtonPress") ;
6423 
6424          doing_icor = 0 ;
6425 
6426          /* don't allow button presses in a recorder window, or in zoom-pan mode */
6427 
6428          if( seq->record_mode || seq->zoom_button1 ){
6429            /* if( seq->record_mode || event->button != Button1 ) XBell(seq->dc->display,100); */
6430            busy=0; EXRETURN;
6431          }
6432 
6433          but = event->button ;
6434 
6435          /* button press in the wbar => popup menu */
6436 
6437          if( w == seq->wbar ){          /* moved here 18 Oct 2001 */
6438            if( but == Button1 ){ /* 21 Oct 2003 */
6439              bx = seq->opt.free_aspect ; seq->opt.free_aspect = 0 ;
6440              ISQ_reset_dimen( seq, seq->last_width_mm, seq->last_height_mm ) ;
6441              seq->opt.free_aspect = bx ;
6442            } else if( but == Button3 ){
6443              XmMenuPosition( seq->wbar_menu , event ) ; /* where */
6444              XtManageChild ( seq->wbar_menu ) ;         /* popup */
6445            }
6446            else if( but == Button4 || but == Button5 ){ /* Scroll Wheel */
6447               int ddd = (but==Button4) ? -1 : 1 ;
6448               if( scrollwheel_debug ){
6449                 INFO_message("Scrollwheel (wbar): button=%u ; state mask=%xx",but,event->state) ;
6450                 ININFO_message("  (mask: shift=%xx ctrl=%xx mod1=%xx mod2=%xx mod3=%xx mod4=%xx mod5=%xx)" ,
6451                                ShiftMask , ControlMask , Mod1Mask , Mod2Mask , Mod3Mask , Mod4Mask , Mod5Mask ) ;
6452               }
6453               if( (event->state & scrollwheel_tmask) )
6454                 DC_palette_bright(  seq->dc , ddd ) ;   /* brightness */
6455               else
6456                 DC_palette_squeeze( seq->dc , ddd ) ;   /* contrast */
6457               COLORMAP_CHANGE(seq) ;
6458            }
6459            else {
6460 #if 0
6461              XUngrabPointer( event->display , CurrentTime ) ;
6462 #else
6463              /* XBell(seq->dc->display,100) ; */
6464 #endif
6465            }
6466            MCW_discard_events( w , ButtonPressMask ) ;
6467            busy=0; EXRETURN ;
6468          }
6469 
6470          /* below here, button press was in the image */
6471 
6472          seq->last_bx = bx = event->x ;  /* 23 Oct 2003: save last button */
6473          seq->last_by = by = event->y ;  /*            press (x,y) coords */
6474          seq->cmap_changed = 0 ;
6475 
6476          /* 26 Feb 2007: Buttons 4 and 5 = Scroll Wheel = change slice */
6477 
6478          if( but == Button4 || but == Button5 ){
6479            if( seq->button2_enabled ){ busy=0; EXRETURN; }  /* 10 Oct 2007 */
6480            if( scrollwheel_debug ){
6481              INFO_message("Scrollwheel (imag): button=%u ; state mask=%xx",but,event->state) ;
6482              ININFO_message("  (mask: shift=%xx ctrl=%xx mod1=%xx mod2=%xx mod3=%xx mod4=%xx mod5=%xx)" ,
6483                             ShiftMask , ControlMask , Mod1Mask , Mod2Mask , Mod3Mask , Mod4Mask , Mod5Mask ) ;
6484            }
6485            if( (event->state & scrollwheel_tmask) ){ /* mod+scroll == '{}' */
6486              if( scrollwheel_debug ) ININFO_message("  change threshold") ;
6487 STATUS("scroll wheel ==> change threshold") ;
6488              cbs.reason = isqCR_keypress ;
6489              cbs.event  = ev ;
6490              cbs.key    = (but==Button4) ? '}' : '{' ; /* == change threshold */
6491              cbs.nim    = seq->im_nr ;
6492              SEND(seq,cbs) ;
6493            } else {                           /* no modifiers == change slice */
6494              int nold=seq->im_nr , dd=(but==Button4)?-1:+1 , nnew ;
6495              if( scrollwheel_debug ) ININFO_message("  change slice") ;
6496 STATUS("scroll wheel ==> change slice") ;
6497              if( AFNI_yesenv("AFNI_INDEX_SCROLLREV") ) dd = -dd ;
6498              nnew = nold + dd ;
6499              ISQ_timer_stop(seq) ;
6500              if( nnew >= 0 && nnew < seq->status->num_total )
6501                ISQ_redisplay( seq , nnew , isqDR_display ) ;
6502            }
6503            MCW_discard_events( w , ButtonPressMask ) ;
6504            busy=0; EXRETURN;
6505          }
6506 
6507          MCW_widget_geom( w , &width , &height , NULL,NULL ) ;
6508          seq->wimage_width  = width ;
6509          seq->wimage_height = height ;
6510 
6511          MCW_discard_events( w , ButtonPressMask ) ;
6512 
6513          /* 12-17 Jun 2002: Shift+Button2 for picking crop rectangle */
6514 
6515          if( w == seq->wimage &&
6516              ( (but==Button2 && (event->state & ShiftMask)) ||
6517                (seq->crop_drag)                            )  ){
6518 
6519            ISQ_cropper( seq , event ) ;
6520            busy=0; EXRETURN ;
6521 
6522          } /* end of cropping stuff */
6523 
6524          /** 03 Oct 2002: change Shift+Button1 into Button2 **/
6525 
6526          if( but == Button1 &&
6527              ( seq->cursor_state == CURSOR_PENCIL ||
6528                ((event->state & ShiftMask) && !(event->state & ControlMask)) ) )
6529            event->button = but = Button2 ;
6530 
6531          /*-- default processing --*/
6532 
6533          switch( but ){
6534 
6535             case Button3:
6536             case Button1:{
6537               int imx,imy,nim;
6538 
6539               /* while Button2 is active, nothing else is allowed */
6540 
6541               if( seq->button2_active ){
6542                 /*** XBell(seq->dc->display,100) ; ***/
6543                 busy=0; EXRETURN ;
6544               }
6545 
6546               /* Button3 presses in the image with a modifier
6547                  key pressed also means to popup some menu    */
6548 
6549               if( w == seq->wimage && but == Button3 &&
6550                   (event->state & (ShiftMask|ControlMask|Mod1Mask)) ){
6551 
6552                 /* 23 Oct 1996: Simulation of bottom buttons */
6553 
6554                 if( (event->state & ShiftMask) && !(event->state & ControlMask) )
6555                   ISQ_but_disp_CB( seq->wbut_bot[NBUT_DISP] , seq , NULL ) ;
6556 
6557                 else if( (event->state & ControlMask) ){
6558                   if( seq->status->num_total > 1 && !(event->state & ShiftMask) ){
6559                     ISQ_montage_CB( seq->wbut_bot[NBUT_MONT] , seq , NULL ) ;
6560                   } else {
6561                     XmMenuPosition( seq->wbar_menu , event ) ;
6562                     XtManageChild ( seq->wbar_menu ) ;
6563                   }
6564                 }
6565 
6566                 else if( (seq->opt.save_one || seq->status->num_total > 1)
6567                          && (event->state & Mod1Mask) )
6568                    ISQ_but_save_CB( seq->wbut_bot[NBUT_SAVE] , seq , NULL ) ;
6569 
6570                 /* else
6571                    XBell( seq->dc->display , 100 ) ; */
6572 
6573               /* Button1: compute the location in the image
6574                  where the button event transpired, and send to AFNI */
6575 
6576               } else if( w == seq->wimage && seq->status->send_CB != NULL ){
6577 
6578                 seq->wimage_width = -1 ;
6579                 ISQ_mapxy( seq , bx,by , &imx,&imy,&nim ) ;
6580                 cbs.reason = isqCR_buttonpress ;
6581                 cbs.event  = ev ;
6582                 cbs.xim    = imx ;
6583                 cbs.yim    = imy ;
6584                 cbs.nim    = nim ;
6585 
6586                 doing_icor = ( (event->state&ShiftMask) && (event->state&ControlMask) ) ;
6587 
6588                 if( but == Button1 &&
6589                     (event->state & ControlMask) && !(event->state & ShiftMask) ){ /* 18 Oct 2001 */
6590                    event->button = Button3 ;        /* fake Button3 press */
6591                 }
6592 
6593                 if( event->button == Button3 ||     /* 04 Nov 2003: only for Button3 */
6594                     (event->button == Button1 && event->state&ShiftMask && event->state&ControlMask) )
6595 #if 0
6596                   seq->status->send_CB( seq , seq->getaux , &cbs ) ;
6597 #else
6598                   SEND(seq,cbs) ;
6599 #endif
6600               }
6601             }
6602             break ;
6603 
6604             /* pass this event to the separate handler, if allowed */
6605 
6606             case Button2:{
6607 
6608               /* drawing mode */
6609 
6610               if( seq->button2_enabled && w == seq->wimage )
6611                  ISQ_button2_EV( w , client_data , ev , continue_to_dispatch ) ;
6612               else
6613                  { /* XBell(seq->dc->display,100); */ busy=0; EXRETURN; }
6614             }
6615             break ;
6616 
6617             default: break ;
6618          }
6619       }
6620       ISQ_but_done_reset( seq ) ;
6621       break ;                     /*--- end of ButtonPress ---*/
6622 
6623       /*----- window changed size -----*/
6624 
6625       case ConfigureNotify:{
6626          XConfigureEvent *event = (XConfigureEvent *) ev ;
6627 
6628          static int am_active = 0 ; /* 09 Oct 1999 */
6629 
6630 #if 0
6631          /* 04 Nov 2003: don't do anything while mouse is down */
6632          /* [doesn't work well - usually prevents anything at all] */
6633 
6634          { Window rW,cW ; int rx,ry,x,y ; unsigned int mask ;
6635            XQueryPointer(XtDisplay(w),XtWindow(w),&rW,&cW,&rx,&ry,&x,&y,&mask) ;
6636            if( mask & (Button1Mask|Button2Mask|Button3Mask) ) break ;
6637          }
6638 #endif
6639 
6640          if( am_active ) break ;      /* prevent recursion */
6641          am_active = 1 ;
6642 
6643 #if 0
6644          /* Scan forward and see if another such event is
6645             pending.  If so, don't process this one.
6646             [doesn't work well - can result in not redrawing at proper size?] */
6647 
6648          { XEvent evjunk ; int egood ;
6649            egood = XCheckWindowEvent(XtDisplay(w),XtWindow(w),StructureNotifyMask,&evjunk) ;
6650            if( egood && evjunk.type == ConfigureNotify ){
6651              am_active = 0 ; break ;
6652            }
6653          }
6654 #endif
6655 
6656 
6657  if(PRINT_TRACING){
6658   char str[256] ;
6659   sprintf(str," .. ConfigureNotify: width=%d height=%d",
6660           event->width,event->height);
6661   STATUS(str) ;
6662  }
6663 
6664          /* simply delete the XImage sized to the window;
6665             redisplay will then automatically size it when called */
6666 
6667          if( w == seq->wimage ){
6668 
6669             int nx,ny ;
6670 #if 0
6671             int ntime=NI_clock_time(); /* 09 May 2018 [useless?] */
6672             static int ltime=-666;
6673             if( ntime-ltime < 2 ){ am_active = 0 ; break ; } /* too fast? */
6674             ltime = ntime ;
6675 #endif
6676 
6677             MCW_widget_geom( seq->wimage , &nx , &ny , NULL,NULL ) ;
6678             if( (seq->sized_xim == NULL)       ||
6679                 (nx != seq->sized_xim->width ) ||    /* modified 09 May 2018 */
6680                 (ny != seq->sized_xim->height)   ){  /* to check nx and ny */
6681 
6682                seq->wimage_width = seq->wimage_height = -1 ; /* Feb 1998 */
6683 
6684                KILL_2ndXIM( seq->given_xim , seq->sized_xim ) ;
6685 
6686                /*-- 09 Oct 1999: if ordered, enforce aspect --*/
6687                /*-- 21 Oct 2003: only if it's been a while  --*/
6688 
6689 #if 0
6690 fprintf(stderr,"ConfigureNotify: width=%d height=%d\n",event->width,event->height);
6691 #endif
6692 
6693 #if 0 /* removed 10 May 2018 */
6694                if( AFNI_yesenv("AFNI_ENFORCE_ASPECT") && !seq->opt.free_aspect ){
6695                  static int last_time=-666 ; int now_time=NI_clock_time() ;
6696                  if( now_time-last_time > 555 )
6697                    ISQ_reset_dimen( seq, seq->last_width_mm, seq->last_height_mm ) ;
6698 #if 0
6699 else fprintf(stderr,"  -- too soon to enforce aspect!\n") ;
6700 #endif
6701                  last_time = now_time ;
6702                }
6703 #endif
6704 
6705                /*-- now show the image in the new window size --*/
6706 
6707 #if 0
6708 INFO_message("ConfigureNotify") ;
6709 #endif
6710                ISQ_show_image( seq ) ;
6711             } else {
6712 #if 0
6713 INFO_message("reject image ConfigureNotify") ;
6714 #endif
6715             }
6716 
6717          } else if( w == seq->wbar ){
6718              int nx,ny ;
6719              MCW_widget_geom( seq->wbar , &nx , &ny , NULL,NULL ) ;
6720              if( (seq->sized_xbar == NULL)       ||
6721                  (nx != seq->sized_xbar->width ) ||
6722                  (ny != seq->sized_xbar->height)   ){
6723 
6724                KILL_2ndXIM( seq->given_xbar , seq->sized_xbar ) ;
6725                ISQ_show_bar( seq ) ;
6726             } else {
6727 #if 0
6728 INFO_message("reject wbar ConfigureNotify") ;
6729 #endif
6730             }
6731          }
6732 
6733          am_active = 0 ;
6734       }
6735       break ;  /*--- end of ConfigureNotify ---*/
6736 
6737       /*----- ignore all other events -----*/
6738 
6739       default: break ;
6740 
6741    } /* end of switch ev->type */
6742 
6743    busy=0; EXRETURN ;
6744 }
6745 
6746 /*-----------------------------------------------------------------------
6747    Handle Button2 events in the image window -- Feb 1998
6748 -------------------------------------------------------------------------*/
6749 
6750 #define NPTS_MAX 4095  /* max # points in a single button2 operation */
6751 
ISQ_button2_EV(Widget w,XtPointer client_data,XEvent * ev,RwcBoolean * continue_to_dispatch)6752 void ISQ_button2_EV( Widget w , XtPointer client_data ,
6753                      XEvent *ev , RwcBoolean *continue_to_dispatch )
6754 {
6755    MCW_imseq *seq = (MCW_imseq *) client_data ;
6756    ISQ_cbs cbs ;
6757    static int nsav ;
6758    static int *bxsav=NULL , *bysav=NULL , *xyout=NULL ;
6759 
6760 ENTRY("ISQ_button2_EV") ;
6761 
6762    /* check for legality */
6763 
6764    if( !ISQ_REALZ(seq) || !seq->button2_enabled || w != seq->wimage ) EXRETURN ;
6765 
6766    ISQ_timer_stop(seq) ;
6767 
6768    switch( ev->type ){
6769 
6770       /*----- take button press -----*/
6771 
6772       case ButtonPress:{
6773          XButtonEvent *event = (XButtonEvent *) ev ;
6774          int bx,by , but , xim,yim,zim ;
6775 
6776          but = event->button ; if( but != Button2 ) EXRETURN ;
6777 
6778          seq->button2_active = 1 ;  /* allow other button2 stuff to happen */
6779 
6780          /* 1st time in: allocate space to save points */
6781 
6782          if( bxsav == NULL ){
6783            bxsav = (int *) malloc( sizeof(int) * (NPTS_MAX+1) ) ;
6784            bysav = (int *) malloc( sizeof(int) * (NPTS_MAX+1) ) ;
6785          }
6786 
6787          /* save this point */
6788 
6789          bx = event->x ; by = event->y ;
6790          bxsav[0] = bx ; bysav[0] = by ; nsav = 1 ;
6791 
6792          /* find where this point is in original images --
6793             if it is illegal, quit this mockery of a travesty of a sham */
6794 
6795          seq->wimage_width = -1 ;
6796          ISQ_mapxy( seq , bx,by , &xim,&yim,&zim ) ;
6797          if( xim < 0 || yim < 0 || zim < 0 || zim >= seq->status->num_total ){
6798             seq->button2_active = 0 ;         /* disallow button2 stuff */
6799             XBell( seq->dc->display , 100 ) ; /* express our displeasure */
6800             EXRETURN ;
6801          }
6802 
6803          /* draw this point */
6804 
6805          if( seq->button2_drawmode != BUTTON2_NODRAW ){
6806             DC_fg_colorpix( seq->dc , seq->button2_pixel ) ;
6807             XDrawPoint( seq->dc->display , XtWindow(seq->wimage) ,
6808                         seq->dc->myGC , bx,by ) ;
6809          }
6810       }
6811       break ;
6812 
6813       /*----- take button release -----*/
6814 
6815       case ButtonRelease:{
6816          XButtonEvent *event = (XButtonEvent *) ev ;
6817          int bx,by ;
6818          int ii,nout , nim , xim,yim,zim ;
6819 
6820          /* check for legality  */
6821 
6822          if( !seq->button2_active || event->button != Button2 ) EXRETURN ;
6823 
6824          bx = event->x ; by = event->y ;  /* where did it happen? */
6825 
6826          /* if a new point, save it and draw it */
6827 
6828          if( bx != bxsav[nsav-1] || by != bysav[nsav-1] ){
6829 
6830             if( seq->button2_drawmode == BUTTON2_POINTS ){
6831                XDrawPoint( seq->dc->display , XtWindow(seq->wimage) ,
6832                            seq->dc->myGC , bx,by ) ;
6833             } else if( seq->button2_drawmode != BUTTON2_NODRAW ){
6834                if( seq->button2_width > 0 )                     /* 08 Oct 2002 */
6835                  DC_linewidth( seq->dc , seq->button2_width ) ;
6836                XDrawLine( seq->dc->display , XtWindow(seq->wimage) ,
6837                           seq->dc->myGC , bxsav[nsav-1],bysav[nsav-1],bx,by ) ;
6838                if( seq->button2_width > 0 ) DC_linewidth( seq->dc , 0 ) ;
6839             }
6840 
6841             bxsav[nsav] = bx ; bysav[nsav] = by ;
6842             if( nsav < NPTS_MAX ) nsav++ ;
6843          }
6844 
6845          /* this is the last point in this sequence --
6846             if we are drawing closed polygon, then close it now */
6847 
6848          if( seq->button2_drawmode == BUTTON2_CLOSEDPOLY && nsav > 2 ){
6849             if( seq->button2_width > 0 )                     /* 08 Oct 2002 */
6850               DC_linewidth( seq->dc , seq->button2_width ) ;
6851             XDrawLine( seq->dc->display , XtWindow(seq->wimage) ,
6852                        seq->dc->myGC , bxsav[nsav-1],bysav[nsav-1] ,
6853                                        bxsav[0]     ,bysav[0]       ) ;
6854             if( seq->button2_width > 0 ) DC_linewidth( seq->dc , 0 ) ;
6855 
6856             /* and add the 1st point to the list again */
6857 
6858             bxsav[nsav] = bxsav[0] ; bysav[nsav] = bysav[0] ;
6859             if( nsav < NPTS_MAX ) nsav++ ;
6860          }
6861 
6862          /* 1st time here: make space for output list */
6863 
6864          if( xyout == NULL )
6865             xyout = (int *) malloc( sizeof(int) * 2*NPTS_MAX ) ;
6866 
6867          /* now assemble output list of (x,y) pairs,
6868             in the original image grid --
6869             but only save points that are in the same image as the 1st point */
6870 
6871          seq->wimage_width = -1 ;
6872          ISQ_mapxy( seq , bxsav[0] , bysav[0] , &xim,&yim,&zim ) ;
6873          nim = zim ; xyout[0] = xim ; xyout[1] = yim ; nout = 1 ;
6874          for( ii=1 ; ii < nsav ; ii++ ){
6875             ISQ_mapxy( seq , bxsav[ii] , bysav[ii] , &xim,&yim,&zim ) ;
6876             if( zim == nim && xim >= 0 && yim >= 0 ){
6877                xyout[2*nout] = xim ; xyout[2*nout+1] = yim ;
6878                nout++ ;
6879             }
6880          }
6881 
6882          /* send to the almighty AFNI */
6883 
6884          cbs.reason   = isqCR_button2_points ;
6885          cbs.event    = ev ;
6886          cbs.key      = ii ;                 /* number of points */
6887          cbs.nim      = nim ;                /* z coord */
6888          cbs.userdata = (XtPointer) xyout ;  /* x & y coords */
6889 #if 0
6890          seq->status->send_CB( seq , seq->getaux , &cbs ) ;
6891 #else
6892          SEND(seq,cbs) ;
6893 #endif
6894 
6895          seq->button2_active = 0 ;  /* disallow button2 stuff */
6896       }
6897       break ;
6898 
6899       /*----- take motion events:
6900               this is minimal so as to keep up with mouse movements -----*/
6901 
6902       case MotionNotify:{
6903          XMotionEvent *event = (XMotionEvent *) ev ;
6904          int bx,by ;
6905 
6906          /* check for legality */
6907 
6908          if( !seq->button2_active || (event->state & Button2Mask) == 0 ) EXRETURN ;
6909 
6910          /* if point is redundant with last one, skip it */
6911 
6912          bx = event->x ; by = event->y ;
6913          if( bx == bxsav[nsav-1] && by == bysav[nsav-1] ) EXRETURN ;
6914 
6915          /* draw point or line to point */
6916 
6917          if( seq->button2_drawmode == BUTTON2_POINTS ){
6918             XDrawPoint( seq->dc->display , XtWindow(seq->wimage) ,
6919                         seq->dc->myGC , bx,by ) ;
6920          } else if( seq->button2_drawmode != BUTTON2_NODRAW ){
6921             if( seq->button2_width > 0 )                     /* 08 Oct 2002 */
6922               DC_linewidth( seq->dc , seq->button2_width ) ;
6923             XDrawLine( seq->dc->display , XtWindow(seq->wimage) ,
6924                        seq->dc->myGC , bxsav[nsav-1],bysav[nsav-1],bx,by ) ;
6925             if( seq->button2_width > 0 ) DC_linewidth( seq->dc , 0 ) ;
6926          }
6927 
6928          /* save it */
6929 
6930          bxsav[nsav] = bx ; bysav[nsav] = by ;
6931          if( nsav < NPTS_MAX ) nsav++ ;
6932       }
6933       break ;
6934 
6935    }
6936    EXRETURN ;
6937 }
6938 
6939 /*---------------------------------------------------------------------
6940    process Disp button press for an imseq:
6941      change the way the image is displayed (flip, rotate, ...),
6942      by popping up a dialog
6943 -----------------------------------------------------------------------*/
6944 
ISQ_but_disp_CB(Widget w,XtPointer client_data,XtPointer call_data)6945 void ISQ_but_disp_CB( Widget w, XtPointer client_data, XtPointer call_data )
6946 {
6947    MCW_imseq *seq = (MCW_imseq *) client_data ;
6948    int ib ;
6949    Widget rctop , rcboxes , shtop ;
6950    Widget swtop=NULL ;
6951 
6952 ENTRY("ISQ_but_disp_CB") ;
6953 
6954    if( ! ISQ_REALZ(seq) || seq->dialog != NULL ) EXRETURN ;
6955 
6956    for( ib=0 ; ib < NBUTTON_BOT-1 ; ib++ )       /* turn off buttons  */
6957      if( ISQ_but_bot_dial[ib] == True )          /* that also want to */
6958        SENSITIZE( seq->wbut_bot[ib] , False ) ;  /* use seq->dialog   */
6959 
6960    seq->dialog = XtVaCreatePopupShell(
6961                     "font8" , xmDialogShellWidgetClass , seq->wtop ,
6962                        XmNtitle , "Display Options" ,
6963                        XmNdeleteResponse , XmDO_NOTHING ,
6964                        XmNinitialResourcesPersistent , False ,
6965                     NULL ) ;
6966 
6967    SAVEUNDERIZE(seq->dialog) ; /* 27 Feb 2001 */
6968 
6969    DC_yokify( seq->dialog , seq->dc ) ;  /* 14 Sep 1998 */
6970 
6971    seq->dialog_starter = NBUT_DISP ;
6972 
6973 #if 1
6974    if( MCW_isitmwm(w) )
6975       XtVaSetValues( seq->dialog ,
6976                        XmNmwmDecorations , MWM_DECOR_BORDER ,
6977                        XmNmwmFunctions ,   MWM_FUNC_MOVE
6978                                          | MWM_FUNC_CLOSE ,
6979                      NULL ) ;
6980 #endif
6981 
6982    XmAddWMProtocolCallback(           /* make "Close" window menu work */
6983            seq->dialog ,
6984            XmInternAtom( seq->dc->display , "WM_DELETE_WINDOW" , False ) ,
6985            ISQ_disp_act_CB , seq ) ;
6986 
6987    for( ib=0 ; ib < NACT_DISP ; ib++ )
6988       ISQ_disp_act[ib].data = (XtPointer) seq ;
6989 
6990    if( seq->dc->height < 1024 ||               /* 21 Jun 2005 */
6991        AFNI_yesenv("AFNI_DISP_SCROLLBARS") ){  /* 31 Jan 2002 */
6992 
6993       shtop = swtop = XtVaCreateManagedWidget(
6994                  "menu" , xmScrolledWindowWidgetClass , seq->dialog ,
6995                     XmNscrollingPolicy        , XmAUTOMATIC ,
6996                     XmNvisualPolicy           , XmVARIABLE ,
6997                     XmNscrollBarDisplayPolicy , XmAS_NEEDED /* XmSTATIC */ ,
6998                     XmNinitialResourcesPersistent , False ,
6999                  NULL ) ;
7000    } else {
7001       shtop = seq->dialog ;
7002    }
7003 
7004    rctop = XtVaCreateWidget(
7005               "menu" , xmRowColumnWidgetClass , shtop ,
7006                  XmNpacking    , XmPACK_TIGHT ,
7007                  XmNnumColumns , 1 ,
7008 
7009                  XmNinitialResourcesPersistent , False ,
7010               NULL ) ;
7011 
7012    rcboxes = XtVaCreateWidget(
7013                 "menu" , xmRowColumnWidgetClass , rctop ,
7014                    XmNpacking    , XmPACK_TIGHT ,
7015                    XmNnumColumns , 2 ,
7016 
7017                    XmNinitialResourcesPersistent , False ,
7018               NULL ) ;
7019 
7020    for( ib=0 ; ib < NBOX_DISP ; ib++ ){
7021       int jh ;
7022       char **bbh = ISQ_bb_allhelp[ib] ;
7023       char **cch = ISQ_bb_allhint[ib] ;
7024 
7025       /*** 30 Oct 1996: transformations just above the IMPROC buttons ***/
7026 
7027       if( ib == NTOG_IMP ){
7028          int nav = 0 ;
7029 
7030          /*---- FIRST, add some check boxes for special options ----*/
7031 
7032          char *save_one_label[] = { "Save One" }  ; /* 26 Jul 2001 */
7033          char *save_agif_label  = "Save Anim GIF" ; /* 27 Jul 2001 */
7034          char *save_mpeg_label  = "Save Anim MPG" ; /* 02 Aug 2001 */
7035          char *save_anim_label[2] ;
7036 
7037          seq->save_one_bbox = new_MCW_bbox( rcboxes ,
7038                                             1 ,
7039                                             save_one_label ,
7040                                             MCW_BB_check ,
7041                                             MCW_BB_frame ,
7042                                             ISQ_disp_act_CB , (XtPointer) seq ) ;
7043          MCW_reghelp_children( seq->save_one_bbox->wrowcol ,
7044                                " \n"
7045                                "When pressed IN, then the 'Save' button\n"
7046                                "will only save a snapshot of the current\n"
7047                                "display.  This is the ONLY way to save\n"
7048                                "a montage.\n"
7049                                "\n"
7050                                "When pressed OUT, then the 'Save' button\n"
7051                                "asks for the first and last image indexes\n"
7052                                "to save, and then saves each individual\n"
7053                                "image (no montage) to a file.\n"
7054                              ) ;
7055          MCW_reghint_children( seq->save_one_bbox->wrowcol ,
7056                                "Save just 1 (including montage)" ) ;
7057 
7058          if( ppmto_agif_filter != NULL || ppmto_mpeg_filter != NULL ){
7059            int nb = 0 ;
7060            if( ppmto_agif_filter != NULL ) save_anim_label[nb++]=save_agif_label;
7061            if( ppmto_mpeg_filter != NULL ) save_anim_label[nb++]=save_mpeg_label;
7062            seq->save_agif_bbox = new_MCW_bbox( rcboxes ,
7063                                                nb ,
7064                                                save_anim_label ,
7065                                                MCW_BB_radio_zero ,
7066                                                MCW_BB_frame ,
7067                                                ISQ_disp_act_CB, (XtPointer)seq );
7068            MCW_reghelp_children( seq->save_agif_bbox->wrowcol ,
7069                                  " \n"
7070                                  "Controls if image sequence is saved to\n"
7071                                  "an animation file, rather than a bunch\n"
7072                                  "of separate image files.\n"
7073                                  "* This takes precedence over 'Save One',\n"
7074                                  "    if it is also turned on.\n"
7075                                  "* GIF animations require gifsicle.\n"
7076                                  "* MPEG-1 animations require ffmpeg.\n"
7077                                ) ;
7078            MCW_reghint_children( seq->save_agif_bbox->wrowcol ,
7079                                  "Save image sequence to animation" ) ;
7080          } else {
7081            seq->save_agif_bbox = NULL ;
7082          }
7083 
7084          /*---- OK, do the transforms NOW ----*/
7085 
7086          if( seq->status->slice_proj != NULL &&
7087              seq->status->slice_proj->num > 0  ){  /* 31 Jan 2002 */
7088 
7089              (void) XtVaCreateManagedWidget(
7090                       "menu" , xmSeparatorWidgetClass , rcboxes ,
7091                          XmNseparatorType , XmSINGLE_LINE ,
7092                          XmNinitialResourcesPersistent , False ,
7093                       NULL ) ;
7094 
7095              seq->slice_proj_av =
7096                 new_MCW_optmenu( rcboxes , "Project" ,
7097                                  0 , seq->status->slice_proj->num ,
7098                                  seq->slice_proj_index , 0 ,
7099                                  ISQ_slice_proj_CB , (XtPointer) seq ,
7100                                  ISQ_transform_label ,
7101                                  (XtPointer) seq->status->slice_proj ) ;
7102 
7103              if( seq->status->slice_proj->num >= COLSIZE )
7104                 AVOPT_columnize( seq->slice_proj_av ,
7105                                  (seq->status->slice_proj->num/COLSIZE)+1 ) ;
7106 
7107              MCW_reghelp_children( seq->slice_proj_av->wrowcol ,
7108                                    "Choose a projection function\n"
7109                                    "to apply to plus-or-minus\n"
7110                                    "'Slab' images from each pixel.\n"
7111                                    "Built-in projections:\n"
7112                                    " Minimum = smallest value in slab\n"
7113                                    " Maximum = largest value in slab\n"
7114                                    " Mean    = average value in slab\n"
7115                                    " Median  = median value in slab\n"
7116                                    " Extreme = value farthest from median" ) ;
7117 
7118              MCW_reghint_children( seq->slice_proj_av->wrowcol ,
7119                                    "Image projection function"  ) ;
7120 
7121              seq->slice_proj_range_av =
7122                 new_MCW_optmenu( rcboxes , "Slab +-" ,
7123                                  0 , 19 , seq->slice_proj_range , 0 ,
7124                                  ISQ_slice_proj_CB , (XtPointer) seq ,
7125                                  NULL , NULL ) ;
7126              MCW_reghelp_children( seq->slice_proj_range_av->wrowcol ,
7127                                    "Choose thickness of Project slice\n"
7128                                    "package (in each direction from\n"
7129                                    "central slice).  For example:\n"
7130                                    " 2 ==> slab is 5 images thick\n"
7131                                    "       (2 before, 2 after, central)" ) ;
7132              MCW_reghint_children( seq->slice_proj_range_av->wrowcol ,
7133                                    "Slab half-thickness"              ) ;
7134              nav++ ;
7135          }
7136 
7137          /* 0D transforms */
7138 
7139          if( seq->status->transforms0D != NULL &&
7140              seq->status->transforms0D->num > 0  ){
7141 
7142              (void) XtVaCreateManagedWidget(
7143                       "menu" , xmSeparatorWidgetClass , rcboxes ,
7144                          XmNseparatorType , XmSINGLE_LINE ,
7145                          XmNinitialResourcesPersistent , False ,
7146                       NULL ) ;
7147 
7148              seq->transform0D_av =
7149                 new_MCW_optmenu( rcboxes , "Tran 0D" ,
7150                                  0 , seq->status->transforms0D->num ,
7151                                  seq->transform0D_index , 0 ,
7152                                  ISQ_transform_CB , (XtPointer) seq ,
7153                                  ISQ_transform_label ,
7154                                  (XtPointer) seq->status->transforms0D ) ;
7155 
7156              if( seq->status->transforms0D->num >= COLSIZE )
7157                 AVOPT_columnize( seq->transform0D_av ,
7158                                  (seq->status->transforms0D->num/COLSIZE)+1 ) ;
7159 
7160              MCW_reghelp_children( seq->transform0D_av->wrowcol ,
7161                                    "Choose a function to apply to\n"
7162                                    "each point in the image." ) ;
7163              MCW_reghint_children( seq->transform0D_av->wrowcol ,
7164                                    "Pointwise transformations" ) ;
7165              nav++ ;
7166          }
7167 
7168          /* 2D transforms */
7169 
7170          if( seq->status->transforms2D != NULL &&
7171              seq->status->transforms2D->num > 0  ){
7172 
7173              (void) XtVaCreateManagedWidget(
7174                       "menu" , xmSeparatorWidgetClass , rcboxes ,
7175                          XmNseparatorType , XmSINGLE_LINE ,
7176                          XmNinitialResourcesPersistent , False ,
7177                       NULL ) ;
7178 
7179              seq->transform2D_av =
7180                 new_MCW_optmenu( rcboxes , "Tran 2D" ,
7181                                  0 , seq->status->transforms2D->num ,
7182                                  seq->transform2D_index , 0 ,
7183                                  ISQ_transform_CB , (XtPointer) seq ,
7184                                  ISQ_transform_label ,
7185                                  (XtPointer) seq->status->transforms2D ) ;
7186 
7187              if( seq->status->transforms2D->num >= COLSIZE )
7188                 AVOPT_columnize( seq->transform2D_av ,
7189                                  (seq->status->transforms2D->num/COLSIZE)+1 ) ;
7190 
7191              MCW_reghelp_children( seq->transform2D_av->wrowcol ,
7192                                    "Choose a function to apply to\n"
7193                                    "the underlay image as a whole." ) ;
7194              MCW_reghint_children( seq->transform2D_av->wrowcol ,
7195                                    "Global transformations" ) ;
7196              nav++ ;
7197          }
7198 
7199          /* 30 Dec 1998: rowgraphs */
7200 
7201          if( nav > 0 && seq->status->send_CB != NULL ){
7202             (void) XtVaCreateManagedWidget(
7203                      "menu" , xmSeparatorWidgetClass , rcboxes ,
7204                         XmNseparatorType , XmSINGLE_LINE ,
7205                         XmNinitialResourcesPersistent , False ,
7206                      NULL ) ;
7207 
7208             seq->rowgraph_av =
7209                new_MCW_optmenu( rcboxes , "RowGraphs" ,
7210                                 0 , ROWGRAPH_MAX , seq->rowgraph_num , 0 ,
7211                                 ISQ_rowgraph_CB , (XtPointer) seq ,
7212                                 ISQ_rowgraph_label , NULL ) ;
7213             AVOPT_columnize( seq->rowgraph_av , 2 ) ;
7214 
7215             MCW_reghelp_children( seq->rowgraph_av->wrowcol ,
7216                                   "Rowgraphs are plots of the underlay\n"
7217                                   "(grayscale) image intensity as\n"
7218                                   "x vs. y graphs.  Each graph is from\n"
7219                                   "one displayed horizontal row of the\n"
7220                                   "image.  The bottom rowgraph is from\n"
7221                                   "the image row under the crosshairs.\n"
7222                                   "Upper rowgraphs are from higher image\n"
7223                                   "rows.  Note that image transformations\n"
7224                                   "functions and image rotations/flips\n"
7225                                   "will affect the rowgraphs as well as\n"
7226                                   "the image display.\n\n"
7227                                   "N.B.: The color 'UK Flag' marker indicates\n"
7228                                   "      the crosshair focus point. It can be\n"
7229                                   "      turned off via the 'No Overlay' button."
7230                                  ) ;
7231             MCW_reghint_children( seq->rowgraph_av->wrowcol ,
7232                                   "Number of image rows to graph" ) ;
7233             nav++ ;
7234          }
7235 
7236          /* 21 Jan 1999: surfgraph */
7237 
7238          if( nav > 0 && seq->status->send_CB != NULL ){
7239             (void) XtVaCreateManagedWidget(
7240                      "menu" , xmSeparatorWidgetClass , rcboxes ,
7241                         XmNseparatorType , XmSINGLE_LINE ,
7242                         XmNinitialResourcesPersistent , False ,
7243                      NULL ) ;
7244 
7245             seq->surfgraph_av =
7246                new_MCW_optmenu( rcboxes , "SurfGraph" ,
7247                                 0 , SURFGRAPH_MAX , seq->surfgraph_num , 0 ,
7248                                 ISQ_surfgraph_CB , (XtPointer) seq ,
7249                                 ISQ_surfgraph_label , NULL ) ;
7250 
7251             MCW_reghelp_children( seq->surfgraph_av->wrowcol ,
7252                                   "The SurfGraph is a wiremesh plot of the\n"
7253                                   "underlay (grayscale) image intensity vs.\n"
7254                                   "x and y.  Use the arrows in the SurfGraph\n"
7255                                   "window to rotate the viewpoint; use the\n"
7256                                   "middle button between the arrows to reset\n"
7257                                   "the viewpoint to the default orientation.\n"
7258                                   "\n"
7259                                   "N.B.: The plotting routine may produce some\n"
7260                                   "        erroneous vertical lines on occasion.\n"
7261                                   "      The color 'UK Flag' marker indicates\n"
7262                                   "        crosshair focus point.  It is drawn\n"
7263                                   "        on top of the surface at the end, and\n"
7264                                   "        so is always visible, even if it should\n"
7265                                   "        be hidden behind the surface; that is,\n"
7266                                   "        it shines through, no matter what.\n"
7267                                   "      The color marker can be turned off with\n"
7268                                   "        the 'No Overlay' button."
7269                                  ) ;
7270             MCW_reghint_children( seq->surfgraph_av->wrowcol ,
7271                                   "Plot wiremesh surface?" ) ;
7272             nav++ ;
7273          }
7274 
7275          /* final separator */
7276 
7277          if( nav ) (void) XtVaCreateManagedWidget(
7278                             "menu" , xmSeparatorWidgetClass , rcboxes ,
7279                                XmNseparatorType , XmSINGLE_LINE ,
7280                                XmNinitialResourcesPersistent , False ,
7281                             NULL ) ;
7282       }
7283 
7284       /*** back to the button box stuff ***/
7285 
7286       seq->bbox[ib] = new_MCW_bbox( rcboxes ,
7287                                      ISQ_dispbb[ib].nbut ,
7288                                      ISQ_dispbb[ib].lbut ,
7289                                      ISQ_dispbb[ib].type ,
7290                                      ISQ_dispbb[ib].frame ,
7291                                      ISQ_disp_act_CB , (XtPointer) seq ) ;
7292 
7293       seq->bbox[ib]->parent = (XtPointer) seq ;
7294 
7295       seq->num_bbox ++ ;
7296 
7297       for( jh=0 ; jh < seq->bbox[ib]->nbut ; jh++ ){
7298          MCW_register_help( seq->bbox[ib]->wbut[jh] , bbh[jh] ) ;
7299          MCW_register_hint( seq->bbox[ib]->wbut[jh] , cch[jh] ) ;
7300       }
7301 
7302    } /* end of loop creating widgets in Disp */
7303 
7304 #ifdef NO_GROUP_SCALE
7305    XtUnmanageChild( seq->bbox[NTOG_SCL]->wtop ) ;  /* turn this box off! */
7306 #endif
7307 
7308    if( seq->last_image_type != MRI_complex )
7309      XtUnmanageChild( seq->bbox[NTOG_CX]->wtop ) ;
7310 
7311    XtManageChild( rcboxes ) ;
7312 
7313    (void) MCW_action_area( rctop , ISQ_disp_act , NACT_DISP ) ;
7314 
7315    XtManageChild( rctop ) ;
7316 
7317    if( swtop != NULL ){       /* 31 Jan 2002 */
7318      int wx,hy , cmax ;
7319      MCW_widget_geom( rctop  , &wx,&hy,NULL,NULL ) ;
7320      cmax = seq->dc->height-128 ; if( hy > cmax ) hy = cmax ;
7321      XtVaSetValues( seq->dialog , XmNwidth,wx+33,XmNheight,hy+19 , NULL ) ;
7322    }
7323 
7324    ISQ_place_dialog( seq ) ;  /* 05 Jan 1999 */
7325 
7326    XtPopup( seq->dialog , XtGrabNone ) ; NI_sleep(1);
7327 
7328    if( seq->top_clip <= 0.0f ) ALLOW_CLIPPING( seq , 0 ) ;
7329 
7330    ISQ_disp_options( seq , False ) ;  /* set toggles from option list */
7331    seq->save_opt = seq->opt ;         /* for use with Reset button */
7332 
7333    NORMAL_cursorize( seq->dialog ) ;
7334 
7335    ISQ_but_done_reset( seq ) ;
7336    EXRETURN ;
7337 }
7338 
7339 /*-----------------------------------------------------------------------
7340    05 Jan 1999: place the dialog near the image window
7341 -------------------------------------------------------------------------*/
7342 
ISQ_place_dialog(MCW_imseq * seq)7343 void ISQ_place_dialog( MCW_imseq *seq )
7344 {
7345    if( ISQ_REALZ(seq) && !seq->dont_place_dialog )
7346      ISQ_place_widget( seq->wtop , seq->dialog ) ;
7347 
7348    return ;
7349 }
7350 
7351 /*-----------------------------------------------------------------------*/
7352 
ISQ_place_widget(Widget wmain,Widget w)7353 void ISQ_place_widget( Widget wmain , Widget w )  /* 27 Oct 2003 */
7354 {
7355    int dw,dh,dx,dy , xp,yp , wx,hy,xx,yy , sh,sw ;
7356 
7357 ENTRY("ISQ_place_widget") ;
7358 
7359    if( wmain == (Widget)NULL || w == (Widget)NULL ) EXRETURN ;
7360    if( !XtIsRealized(wmain)  || !XtIsRealized(w)  ) EXRETURN ;
7361 
7362    MCW_widget_geom( wmain , &wx,&hy,&xx,&yy ) ;  /* geometry of shell */
7363    MCW_widget_geom( w     , &dw,&dh,&dx,&dy ) ;  /* of dialog */
7364 
7365    sh = HeightOfScreen(XtScreen(wmain)) ;
7366    sw = WidthOfScreen (XtScreen(wmain)) ;
7367 
7368    xp = xx+wx+8 ;
7369    if( xp+dw > sw ) xp = xx-dw-8 ;
7370    if( xp    < 0  ) xp = 0 ;
7371 
7372    yp = yy-4 ;
7373    if( yp+dh > sh ) yp = sh - dh ;
7374    if( yp    < 0  ) yp = 0 ;
7375 
7376    RWC_xineramize( XtDisplay(wmain) , xp,yp,dw,dh , &xp,&yp ); /* 27 Sep 2000 */
7377 
7378    XtVaSetValues( w , XmNx , xp , XmNy , yp , NULL ) ;
7379    EXRETURN ;
7380 }
7381 
7382 /*-----------------------------------------------------------------------
7383   Callback for button and toggle actions in the display dialog
7384 -------------------------------------------------------------------------*/
7385 
ISQ_disp_act_CB(Widget w,XtPointer client_data,XtPointer call_data)7386 void ISQ_disp_act_CB( Widget w, XtPointer client_data, XtPointer call_data )
7387 {
7388    MCW_imseq *seq           = (MCW_imseq *)client_data ;
7389    XmAnyCallbackStruct *cbs = (XmAnyCallbackStruct *)call_data ;
7390 
7391    int ib , close_window ;
7392    char *wname ;
7393    RwcBoolean new_opt = False ;
7394 
7395 #ifdef FLASH_TOGGLE
7396    RwcBoolean flasher ;
7397 #endif
7398 
7399 ENTRY("ISQ_disp_act_CB") ;
7400 
7401    if( !ISQ_REALZ(seq) || seq->dialog==NULL || seq->dialog_starter!=NBUT_DISP ) EXRETURN ;
7402 
7403    wname = XtName(w) ;
7404 
7405    for( ib=0 ; ib < NACT_DISP ; ib++ )           /* button index, if any */
7406      if( strcmp(wname,ISQ_disp_act[ib].label) == 0 ) break ;
7407 
7408    close_window = (ib == DISP_OK)  /* button to exit */
7409                  ||
7410                   ( cbs->reason != XmCR_ACTIVATE       &&   /* exit if */
7411                     cbs->reason != XmCR_DISARM           ); /* not button */
7412 
7413 #ifdef FLASH_TOGGLE
7414    flasher = (cbs->reason == XmCR_DISARM) && (!close_window) ;
7415    if( flasher ) MCW_invert_widget( w ) ;
7416 #endif
7417 
7418    if( ib == DISP_UNDO ){               /* restore options from entry */
7419       seq->opt = seq->save_opt ;        /* and then set toggles */
7420       ISQ_disp_options( seq , False ) ;
7421       new_opt = True ;
7422       AV_SENSITIZE(seq->ov_opacity_av,!seq->opt.no_overlay) ; /* 09 Mar 2001 */
7423 
7424    } else {                                     /* any other activation: */
7425       new_opt = ISQ_disp_options( seq , True ); /* --> set options */
7426    }
7427 
7428    if( close_window ){                          /* close the window */
7429       XtDestroyWidget( seq->dialog ) ; NI_sleep(1) ;
7430       seq->dialog = NULL ;
7431       for( ib=0 ; ib < NBUTTON_BOT-1 ; ib++ )       /* turn buttons back on */
7432          if( ISQ_but_bot_dial[ib] == True )         /* that also want to   */
7433            SENSITIZE( seq->wbut_bot[ib] , True ) ;  /* use seq->dialog    */
7434 
7435       for( ib=0 ; ib < seq->num_bbox ; ib++ ) myXtFree( seq->bbox[ib] ) ;
7436       seq->num_bbox = 0 ;
7437       seq->dialog_starter = -1 ;
7438 
7439       FREE_AV( seq->transform0D_av ) ;
7440       FREE_AV( seq->transform2D_av ) ;
7441       FREE_AV( seq->rowgraph_av )    ;
7442       FREE_AV( seq->surfgraph_av )   ;  /* 21 Jan 1999 */
7443    }
7444 
7445    if( new_opt ){
7446      ISQ_redisplay( seq , -1 , isqDR_reimage ) ;  /* redo current image */
7447 
7448       /* 01 Dec 1999: perhaps redraw winfo label */
7449 
7450       if( ISQ_USE_SIDES(seq) ){
7451          seq->im_label[0] = '\0' ;  /* will force redraw */
7452          ISQ_draw_winfo( seq ) ;
7453       }
7454    }
7455 
7456 #ifdef FLASH_TOGGLE
7457    if( flasher ) MCW_invert_widget( w ) ;  /* flash togglebutton */
7458 #endif
7459 
7460    ISQ_but_done_reset( seq ) ;
7461    EXRETURN ;
7462 }
7463 
7464 /*----------------------------------------------------------------------
7465   map the toggle-button states TO   the options if set == True,
7466                                FROM the options if set == False
7467 
7468   in the former case, return True if any options changed, False otherwise
7469   in the latter case, return False always (options ARE unchanged)
7470 ------------------------------------------------------------------------*/
7471 
ISQ_disp_options(MCW_imseq * seq,RwcBoolean set)7472 RwcBoolean ISQ_disp_options( MCW_imseq *seq , RwcBoolean set )
7473 {
7474    int bval[NBOX_DISP] ;
7475    int ib ;
7476 
7477 ENTRY("ISQ_disp_options") ;
7478 
7479    if( !ISQ_VALID(seq) || seq->dialog==NULL || seq->dialog_starter!=NBUT_DISP )
7480      RETURN(False) ;
7481 
7482    if( set ){                         /* set structure from widgets */
7483       ISQ_options inopt = seq->opt ;
7484       RwcBoolean changed ;
7485 
7486       for( ib=0 ; ib < NBOX_DISP ; ib++ )
7487         bval[ib] = MCW_val_bbox( seq->bbox[ib] ) ;
7488 
7489       seq->opt.mirror      = ( bval[NTOG_MIR] & 1 ) != 0 ;
7490 
7491       seq->opt.rot         = bval[NTOG_ROT] ;
7492 
7493       seq->opt.no_overlay  = ( bval[NTOG_COL] & 1 ) != 0 ;
7494 
7495       AV_SENSITIZE(seq->ov_opacity_av,!seq->opt.no_overlay) ; /* 09 Mar 2001 */
7496 
7497       seq->opt.scale_group = bval[NTOG_SCL] ;
7498 
7499       seq->opt.scale_range = bval[NTOG_RNG] ;
7500 
7501       seq->opt.free_aspect = ( bval[NTOG_ASP] & ISQ_ASPECT    ) != 0 ;
7502       seq->opt.save_nsize  = ( bval[NTOG_SAV] & ISQ_SAV_NSIZE ) != 0 ;
7503       seq->opt.save_pnm    = ( bval[NTOG_SAV] & ISQ_SAV_PNM   ) != 0 ;
7504 
7505       seq->opt.save_one    = MCW_val_bbox(seq->save_one_bbox)   != 0 ; /* 26 Jul 2001 */
7506 
7507       seq->opt.save_agif = seq->opt.save_mpeg = 0 ;
7508       if( seq->save_agif_bbox != NULL ){
7509          int bv = MCW_val_bbox(seq->save_agif_bbox) ;
7510                                                         /* 07 Apr 2005: oops */
7511          switch( bv ){        /* need to handle case when agif isn't allowed */
7512            case 1:
7513                   if( ppmto_agif_filter != NULL ) seq->opt.save_agif = 1 ;
7514              else if( ppmto_mpeg_filter != NULL ) seq->opt.save_mpeg = 1 ;
7515            break ;
7516 
7517            case 2:
7518                   if( ppmto_mpeg_filter != NULL ) seq->opt.save_mpeg = 1 ;
7519            break ;
7520          }
7521       }
7522 
7523       seq->opt.save_filter = -1 ;
7524       if( bval[NTOG_SAV] > ISQ_SAV_PNM && ppmto_num > 0 ){  /* 27 Jun 2001 */
7525          int ii ;
7526          for( ii=0 ; ii < ppmto_num ; ii++ ){
7527             if( bval[NTOG_SAV] == ppmto_bval[ii] ){
7528                seq->opt.save_filter = ii ; break ;
7529             }
7530          }
7531       }
7532 
7533       SET_SAVE_LABEL(seq) ;
7534 
7535       seq->opt.improc_code = bval[NTOG_IMP] ;
7536 
7537       seq->opt.cx_code = bval[NTOG_CX] ;
7538 
7539       /*-- sanity checks --*/
7540 
7541       if( seq->opt.rot != ISQ_ROT_0   &&
7542           seq->opt.rot != ISQ_ROT_90  &&
7543           seq->opt.rot != ISQ_ROT_180 &&
7544           seq->opt.rot != ISQ_ROT_270   ) seq->opt.rot = inopt.rot ;
7545 
7546       if( seq->opt.scale_group != ISQ_SCL_AUTO &&
7547           seq->opt.scale_group != ISQ_SCL_GRP )
7548                                seq->opt.scale_group = inopt.scale_group ;
7549 
7550       if( seq->opt.scale_range != ISQ_RNG_CLIPPED ) seq->redo_clip = 0 ;
7551 DPRI("set scale_range =",seq->opt.scale_range) ;
7552 
7553       if( seq->opt.scale_range != ISQ_RNG_MINTOMAX &&
7554           seq->opt.scale_range != ISQ_RNG_02TO98   &&
7555           seq->opt.scale_range != ISQ_RNG_CLIPPED    )
7556                                seq->opt.scale_range = inopt.scale_range ;
7557 
7558       if( seq->opt.scale_range == ISQ_RNG_CLIPPED ){  /* 17 Sep 2007 */
7559         if( seq->top_clip <= 0.0f ){
7560           ALLOW_CLIPPING( seq , 0 ) ;
7561         } else {
7562           seq->redo_clip = 1 ;
7563         }
7564       }
7565 
7566       changed = ! ISQ_OPT_EQUAL( seq->opt , inopt ) ;
7567 
7568       RETURN(changed) ;
7569 
7570    } else {    /* set widgets from structure */
7571 
7572       bval[NTOG_MIR] = (seq->opt.mirror) ? 1 : 0 ;
7573       bval[NTOG_ROT] = seq->opt.rot ;
7574 
7575       bval[NTOG_COL] = (seq->opt.no_overlay << 0 ) ;
7576 
7577       bval[NTOG_SCL] = seq->opt.scale_group ;
7578       bval[NTOG_RNG] = seq->opt.scale_range ;
7579 
7580       if( seq->opt.scale_range == ISQ_RNG_CLIPPED &&
7581           seq->top_clip <= 0.0f                     ){  /* 17 Sep 2007 */
7582 
7583         ALLOW_CLIPPING( seq , 0 ) ;
7584         bval[NTOG_RNG] = seq->opt.scale_range ;
7585       }
7586 
7587       bval[NTOG_ASP] = (seq->opt.free_aspect) ? ISQ_ASPECT    : 0 ;
7588 
7589       bval[NTOG_SAV] = ( (seq->opt.save_nsize)? ISQ_SAV_NSIZE : 0 )
7590                       +( (seq->opt.save_pnm)  ? ISQ_SAV_PNM   : 0 ) ;
7591 
7592       if( seq->opt.save_filter >= 0 && ppmto_num > 0 )       /* 27 Jun 2001 */
7593          bval[NTOG_SAV] = ppmto_bval[seq->opt.save_filter] ;
7594 
7595       bval[NTOG_IMP] = seq->opt.improc_code ;
7596 
7597       bval[NTOG_CX]  = seq->opt.cx_code ;
7598 
7599       for( ib=0 ; ib < NBOX_DISP ; ib++ )
7600         MCW_set_bbox( seq->bbox[ib] , bval[ib] ) ;
7601 
7602       MCW_set_bbox( seq->save_one_bbox ,
7603                     (seq->opt.save_one) ? 1 : 0 ) ; /* 26 Jul 2001 */
7604 
7605       if( seq->save_agif_bbox != NULL ){      /* 07 Apr 2005: oops */
7606          int bv=0 ; /* need to handle case when agif isn't allowed */
7607          if( ppmto_agif_filter != NULL )
7608            bv = (seq->opt.save_agif) + (seq->opt.save_mpeg)*2 ;
7609          else if( ppmto_mpeg_filter != NULL )
7610            bv = (seq->opt.save_mpeg) ;
7611          MCW_set_bbox( seq->save_agif_bbox , bv ) ;
7612       }
7613 
7614       RETURN(False) ;
7615    }
7616 }
7617 
7618 /*----------------------------------------------------------------------*/
7619 /** Group scaling is disabled, and should be removed entirely someday. **/
7620 #ifndef NO_GROUP_SCALE
7621 /*----------------------------------------------------------------------
7622   routines to collect statistics for scaling these images;
7623      ISQ_statify_all   -> do all statistics (individual then global)
7624      ISQ_statistics_WP -> Xt work process to do statistics on one image
7625   *** The functions below should not be removed, though ***
7626      ISQ_statify_one   -> actually do statistics on one image
7627      ISQ_perpoints     -> get the percentage points for 2%-to-98% scaling
7628 ------------------------------------------------------------------------*/
7629 
ISQ_statify_all(MCW_imseq * seq,RwcBoolean stop_on_minmax)7630 void ISQ_statify_all( MCW_imseq *seq , RwcBoolean stop_on_minmax )
7631 {
7632    RwcBoolean done ;
7633    Widget wmsg ;
7634 
7635 ENTRY("ISQ_statify_all") ;
7636 
7637    if( ! ISQ_VALID(seq) ) EXRETURN ;
7638 
7639    /* this routine just drives the work process until it is done */
7640 
7641    if( !seq->glstat->mm_done ){
7642       wmsg = MCW_popup_message( seq->wtop ,
7643                                 "Please Wait.\nComputing Statistics." ,
7644                                 MCW_CALLER_KILL ) ;
7645    } else {
7646       wmsg = MCW_popup_message( seq->wtop ,
7647                                 "Please Wait.\nComputing Histogram." ,
7648                                 MCW_CALLER_KILL ) ;
7649    }
7650 
7651    XBell( seq->dc->display , 100 ) ;
7652 
7653    WATCH_cursorize( seq->wtop ) ;
7654    WATCH_cursorize( wmsg ) ;
7655    if( seq->dialog != NULL )
7656       WATCH_cursorize( seq->dialog ) ;
7657 
7658    XFlush( seq->dc->display ) ;
7659 
7660    if( seq->glstat->worker != 0 ){  /* remove work process, if started */
7661       XtRemoveWorkProc( seq->glstat->worker ) ;
7662       seq->glstat->worker = 0 ;
7663    }
7664 
7665    /**************************************************************/
7666    do{
7667 
7668       done = ISQ_statistics_WP( (XtPointer) seq ) ;
7669       done = done || ( stop_on_minmax && seq->glstat->mm_done ) ;
7670 
7671    } while ( ! done ) ;
7672    /**************************************************************/
7673 
7674    XtDestroyWidget( wmsg ) ; NI_sleep(1) ;
7675 
7676    NORMAL_cursorize( seq->wtop ) ;
7677    if( seq->dialog != NULL )
7678       NORMAL_cursorize( seq->dialog ) ;
7679 
7680    EXRETURN;
7681 }
7682 
7683 /*-----------------------------------------------------------------------*/
7684 
ISQ_statistics_WP(XtPointer client_data)7685 RwcBoolean ISQ_statistics_WP( XtPointer client_data )
7686 {
7687    MCW_imseq *seq = (MCW_imseq *) client_data ;
7688    ISQ_glob_statistics *gl ;
7689 
7690    MRI_IMAGE *im=NULL ;
7691    register int ntot , nser , nn ;
7692 
7693 ENTRY("ISQ_statistics_WP") ;
7694 
7695    if( ! ISQ_VALID(seq) ) RETURN( True );
7696 
7697    gl   = seq->glstat ;
7698    ntot = seq->status->num_total ;  /* image counts */
7699    nser = seq->status->num_series ;
7700 
7701    /*-- first, check if all individual statistics are done --*/
7702 
7703    if( ! gl->mm_done ){  /* not marked as done:  check them */
7704 
7705       for( nn=0 ; nn < ntot ; nn++ )
7706          if( ! seq->imstat[nn].one_done ) break ;
7707 
7708       if( nn >= ntot ){ /* all were done, so finish them off */
7709 
7710          gl->min = seq->imstat[0].min ;
7711          gl->max = seq->imstat[0].max ;
7712          for( nn=1 ; nn < nser ; nn++ ){ /* global: images in the series */
7713             gl->min = MIN( gl->min , seq->imstat[nn].min ) ;
7714             gl->max = MAX( gl->max , seq->imstat[nn].max ) ;
7715          }
7716          ISQ_SCLEV(gl->min,gl->max,seq->dc->ncol_im,gl->scl_mm,gl->lev_mm);
7717          gl->mm_done = True ;
7718 
7719          RETURN( False );  /* continue next time on global histogramming */
7720       }
7721 
7722       /* if here, image nn has yet to be done for local statistics */
7723 
7724 #if 0
7725       im = (MRI_IMAGE *) seq->getim( nn , isqCR_getimage , seq->getaux ) ;
7726 #else
7727       AFNI_CALL_VALU_3ARG( seq->getim , MRI_IMAGE *,im ,
7728                            int,nn , int,isqCR_getimage , XtPointer,seq->getaux ) ;
7729 #endif
7730       if( im != NULL ){
7731         ISQ_statify_one( seq , nn , im ) ; KILL_1MRI(im) ;
7732       }
7733       RETURN( False );   /* continue next time on next un-statted image */
7734    }
7735 
7736    /* all individual statistics are done --> global histogramming  */
7737    /* (note only images in the "series" are used for this purpose) */
7738 
7739    if( ! gl->per_done ){  /* global statistics not marked as done */
7740 
7741       for( nn=0 ; nn < nser ; nn++ )
7742          if( ! seq->imstat[nn].glob_done ) break ;
7743 
7744       if( nn >= nser ){ /* all were done, so finish them off */
7745 
7746          ISQ_perpoints( gl->min,gl->max,gl->hist ,
7747                         &(gl->per02) , &(gl->per98) ) ;
7748 
7749          ISQ_SCLEV( gl->per02 , gl->per98 ,
7750                     seq->dc->ncol_im , gl->scl_per , gl->lev_per ) ;
7751 
7752          gl->per_done = True ;
7753 
7754          RETURN( True );  /* don't need to do any more statistics! */
7755       }
7756 
7757       /* if here, image nn has yet to be done for global histogram */
7758 
7759 #if 0
7760       im = (MRI_IMAGE *) seq->getim( nn , isqCR_getimage , seq->getaux ) ;
7761 #else
7762       AFNI_CALL_VALU_3ARG( seq->getim , MRI_IMAGE *,im ,
7763                            int,nn , int,isqCR_getimage , XtPointer,seq->getaux ) ;
7764 #endif
7765       if( im != NULL ){
7766          ISQ_statify_one( seq , nn , im ) ; KILL_1MRI(im) ;
7767       }
7768       RETURN( False );   /* continue next time on next un-statted image */
7769    }
7770 
7771    /* shouldn't get here, but if do, print a message and stop work process */
7772 
7773    fprintf(stderr,"\a\n*** imseq work process error!\n") ;
7774    RETURN( True );
7775 }
7776 #endif /*---------- NO_GROUP_SCALE not defined --------------------------*/
7777 
7778 /*-----------------------------------------------------------------------
7779    collect statistics on an image and put into location n in table
7780 -------------------------------------------------------------------------*/
7781 
ISQ_statify_one(MCW_imseq * seq,int n,MRI_IMAGE * im)7782 void ISQ_statify_one( MCW_imseq *seq , int n , MRI_IMAGE *im )
7783 {
7784    ISQ_indiv_statistics *st ;
7785    ISQ_glob_statistics  *gl ;
7786    static int hist[NHISTOG] ; /* static to avoid create/destroy overhead */
7787 
7788 ENTRY("ISQ_statify_one") ;
7789 
7790    /* exit if bad data */
7791 
7792    if( ! ISQ_VALID(seq) || n < 0 || n >= seq->status->num_total ) EXRETURN ;
7793 
7794    st = &( seq->imstat[n] ) ;
7795    gl = seq->glstat ;
7796 
7797    if( im->kind == MRI_rgb ) EXRETURN ;  /* 11 Feb 1999 */
7798 
7799    if( ! st->one_done ){  /* must do individual statistics */
7800 
7801       st->min = mri_min( im ) ; floatfix(st->min) ;
7802       st->max = mri_max( im ) ; floatfix(st->max) ;
7803 
7804       ISQ_SCLEV( st->min , st->max ,
7805                  seq->dc->ncol_im , st->scl_mm , st->lev_mm ) ;
7806 
7807       mri_histogram( im , st->min , st->max , True , NHISTOG,hist ) ;
7808 
7809       ISQ_perpoints( st->min,st->max,hist , &(st->per02) , &(st->per98) ) ;
7810 
7811       ISQ_SCLEV( st->per02 , st->per98 ,
7812                  seq->dc->ncol_im , st->scl_per , st->lev_per ) ;
7813 
7814       /* 12 Jan 2004: compute entropy in bits/byte */
7815 
7816       switch( im->kind ){
7817         default:        st->entropy =        mri_entropy8(im) ; break;
7818         case MRI_short:
7819         case MRI_float: st->entropy = 0.5l * mri_entropy16(im); break;
7820       }
7821 
7822       st->one_done = True ;
7823 
7824    } else if( n < seq->status->num_series &&
7825               ! st->glob_done               ){  /* do global */
7826 
7827       mri_histogram( im , gl->min , gl->max , False , NHISTOG , gl->hist ) ;
7828       st->glob_done = True ;
7829    }
7830 
7831    EXRETURN ;
7832 }
7833 
7834 /*-----------------------------------------------------------------------*/
7835 /* Percentage points for the 2%-98% scaling. */
7836 /*-----------------------------------------------------------------------*/
7837 
ISQ_perpoints(float bot,float top,int hist[],float * per02,float * per98)7838 void ISQ_perpoints( float bot , float top ,
7839                     int hist[] , float *per02 , float *per98 )
7840 {
7841    register int ih , nsum , ns02 , ns98 ;
7842    float prev , cur , frac , dbin ;
7843    static int hcum[NHISTOG] ;  /* static to avoid create-destroy overhead */
7844 
7845 ENTRY("ISQ_perpoints") ;
7846 
7847    nsum = 0 ;
7848    for( ih=0 ; ih < NHISTOG ; ih++ ) hcum[ih] = nsum += hist[ih] ;
7849 
7850    ns02 = 0.02 * nsum ;  /* here is where 2% and 98% are fixed */
7851    ns98 = 0.98 * nsum ;
7852    dbin = (top-bot) / NHISTOG ;
7853 
7854    /*-------*/
7855 
7856    for( ih=0 ; ih < NHISTOG ; ih++ ) if( hcum[ih] >= ns02 ) break ;
7857 
7858    if( ih == NHISTOG ) ih-- ;
7859 
7860    prev   = (ih == 0) ? (0.0) : hcum[ih-1] ;
7861    cur    = hcum[ih] ; if( cur <= prev ) cur = 1.01 * prev + 1.0 ;
7862    frac   = ih + (ns02-prev)/(cur-prev) ;
7863    *per02 = bot + dbin * frac ;
7864 
7865    if( *per02 < bot ) *per02 = bot ;
7866 
7867    /*-------*/
7868 
7869    for( ; ih < NHISTOG ; ih++ ) if( hcum[ih] >= ns98 ) break ;
7870 
7871    if( ih == NHISTOG ) ih-- ;
7872 
7873    prev   = (ih == 0) ? (0.0) : hcum[ih-1] ;
7874    cur    = hcum[ih] ; if( cur <= prev ) cur = 1.01 * prev + 1.0 ;
7875    frac   = ih + (ns98-prev)/(cur-prev) ;
7876    *per98 = bot + dbin * frac ;
7877 
7878    if( *per98 > top ) *per98 = top ;
7879 
7880    EXRETURN ;
7881 }
7882 
7883 /*------------------------------------------------------------------------
7884    change the palette based on the arrow actions
7885 --------------------------------------------------------------------------*/
7886 
ISQ_arrow_CB(MCW_arrowval * av,XtPointer client_data)7887 void ISQ_arrow_CB( MCW_arrowval *av , XtPointer client_data )
7888 {
7889    MCW_imseq *seq = (MCW_imseq *) client_data ;
7890    int ddd ;
7891 
7892 ENTRY("ISQ_arrow_CB") ;
7893 
7894    if( ! ISQ_REALZ(seq) ) EXRETURN ;
7895 
7896    if( av->fval > av->old_fval ) ddd = -1 ;
7897    else                          ddd =  1 ;
7898 
7899 /*
7900    ddd = (av->fval > av->old_fval) ? (-1) : (1) ;
7901 */
7902 
7903    if( av == seq->arrow[NARR_SQUEEZE] ){
7904            DC_palette_squeeze( seq->dc , ddd ) ;
7905            COLORMAP_CHANGE(seq) ;      /* 22 Aug 1998 */
7906 
7907    } else if( av == seq->arrow[NARR_BRIGHT]  ){
7908            DC_palette_bright(  seq->dc , ddd ) ;
7909            COLORMAP_CHANGE(seq) ;      /* 22 Aug 1998 */
7910 
7911    } else if( av == seq->arrow[NARR_ROTATE]  ){
7912            DC_palette_rotate(  seq->dc ,-ddd ) ;
7913            COLORMAP_CHANGE(seq) ;      /* 22 Aug 1998 */
7914 
7915    } else if( av == seq->arrow[NARR_GAMMA]   ){
7916            if( seq->imim == NULL || seq->imim->kind != MRI_rgb ){
7917              double new_gamma = seq->dc->gamma ;
7918              if( ddd > 0 ) new_gamma *= 0.95 ;
7919              else          new_gamma /= 0.95 ;
7920              DC_palette_restore( seq->dc , new_gamma ) ;
7921              COLORMAP_CHANGE(seq) ;      /* 22 Aug 1998 */
7922 
7923            } else {   /* 25 Apr 2005: delta gamma on RGB images */
7924              if( ddd > 0 ) seq->rgb_gamma *= 0.95 ;
7925              else          seq->rgb_gamma /= 0.95 ;
7926              ISQ_redisplay( seq , -1 , isqDR_reimage ) ;
7927            }
7928 
7929    } else if( av == seq->arrow[NARR_FRAC]  ){  /* 25 Oct 1996 */
7930       float nfrac = seq->image_frac ;
7931 
7932       nfrac += (ddd < 0) ? DFRAC : -DFRAC ;
7933 
7934       if( nfrac >= FRAC_MIN && nfrac <= FRAC_MAX ){
7935          seq->image_frac = nfrac ;
7936 
7937          XtVaSetValues( seq->wimage ,
7938                           XmNrightPosition ,(int)(0.49 + nfrac * FORM_FRAC_BASE),
7939                           XmNbottomPosition,(int)(0.49 + nfrac * FORM_FRAC_BASE),
7940                         NULL ) ;
7941          XtVaSetValues( seq->wscale ,
7942                           XmNrightPosition ,(int)(0.49 + nfrac * FORM_FRAC_BASE),
7943                         NULL ) ;
7944          XtVaSetValues( seq->wbar ,
7945                           XmNbottomPosition,(int)(0.49 + nfrac * FORM_FRAC_BASE),
7946                         NULL ) ;
7947          XtVaSetValues( seq->winfo ,
7948                           XmNrightPosition ,(int)(0.49 + nfrac * FORM_FRAC_BASE),
7949                         NULL ) ;
7950       } else {
7951          /* XBell( seq->dc->display , 100 ) ; */
7952       }
7953    }
7954 
7955    ISQ_but_done_reset( seq ) ;
7956    EXRETURN ;
7957 }
7958 
7959 /*-----------------------------------------------------------------------
7960    Norm button callback: normalize the palette
7961 -------------------------------------------------------------------------*/
7962 
ISQ_but_cnorm_CB(Widget w,XtPointer client_data,XtPointer call_data)7963 void ISQ_but_cnorm_CB( Widget w, XtPointer client_data, XtPointer call_data )
7964 {
7965    MCW_imseq *seq = (MCW_imseq *) client_data ;
7966 
7967 ENTRY("ISQ_but_cnorm_CB") ;
7968 
7969    if( ! ISQ_REALZ(seq) ) EXRETURN ;
7970 
7971    DC_palette_restore( seq->dc , 0.0 ) ;
7972    seq->rgb_gamma  = 1.0 ;     /* 25 Apr 2005 */
7973    seq->rgb_offset = 0.0 ;
7974    COLORMAP_CHANGE(seq) ;      /* 22 Aug 1998 */
7975    ISQ_but_done_reset( seq ) ;
7976    EXRETURN ;
7977 }
7978 
7979 /*-----------------------------------------------------------------------
7980    External interface to drive an MCW_imseq:
7981      seq        = pointer to structure returned by open_MCW_imseq
7982      drive_code = integer indicating which action to take
7983      drive_data = data or pointer to data controlling action
7984                   (you will probably have to cast this to
7985                    XtPointer to avoid ugly warnings from the compiler)
7986 
7987      drive_code       drive_data should be
7988      ----------       --------------------
7989 *    isqDR_imhelptext (char *) with new help string for image window
7990                         N.B.: this string is copied and so may be
7991                               deleted after the call if you like
7992 
7993 *    isqDR_options    (ISQ_options *) with new options for display
7994 
7995 *    isqDR_numtotal   (int) with new number of images available;
7996                         WARNING: you cannot reduce numtotal above
7997                                  the value num_series in the "status"
7998 
7999 *    isqDR_cursor     (int) with new cursor id for the image window;
8000                        (if negative, means from cursorfont)
8001 
8002 *    isqDR_unrealize  (ignored) the viewer is unrealized [hidden], but
8003                         not destroyed
8004 
8005 *    isqDR_realize    (ignored) an unrealized viewer is re-realized
8006 
8007 *    isqDR_display    (int) call this int "n":
8008                          n <  0  ->  re-get current image and overlay
8009                          n >= 0  ->  move to image # n
8010 
8011 *    isqDR_overlay    (int) call this int "n"
8012                          if n == current image, just re-get overlay
8013                          otherwise, move to image # n
8014                          (setting n=-1 is the same as n=current image)
8015 
8016 *    isqDR_destroy    (ignored) destroy this MCW_imseq, and delete its
8017                         own XtMalloc-ed internal data structures;
8018                         after this call, you must myXtFree(seq) to finish
8019                         the job.
8020 
8021 *    isqDR_arrowpadon (char *) with help string for arrowpad;
8022                         this call turns the arrowpad on; after this, it
8023                         will be visible and send callbacks
8024 
8025 *    isqDR_arrowpadoff (ignored) turn the arrowpad off
8026 
8027 *    isqDR_newseq     (XtPointer) contains new auxiliary data for getim;
8028                         this call switches the image sequence to a
8029                         new one entirely;  this call should be followed
8030                         immediately by one to display the desired
8031                         image from the new sequence!
8032 
8033 *    isqDR_title      (char *) contains new string for window title bar
8034 
8035 *    isqDR_clearstat  (ignored) clears the statistics saved for the
8036                         sequence of images;  usually used for a change
8037                         in the way the images are rendered
8038 
8039 *    isqDR_onoffwid   (int) if 0, turns non-image widgets "off"  (0=isqDR_offwid)
8040                             if 1, turns non-image widgets "on"   (1=isqDR_onwid)
8041                             if 2, toggles their current state    (2=isqDR_togwid)
8042 
8043 *    isqDR_getimnr    (int *) returns the current image index in the sequence
8044                         in the location pointed to by this argument
8045 
8046 *    isqDR_icon       (Pixmap) sets the icon for this window
8047 
8048 *    isqDR_bgicon     (Pixmap) sets the background for this window
8049 
8050 *    isqDR_sendmontage (ignored) tells the MCW_imseq to send the
8051                          montage information back via isqCR_newmontage
8052 
8053 *    isqDR_periodicmont (int) tells whether to use periodic montages
8054 *    isqDR_montmode     (int) sets the montage mode
8055 
8056 *    isqDR_button2_enable  (ignored) tells to enable processing of Button2 events
8057 *    isqDR_button2_disable (ignored) tells to disable such processing
8058 *    isqDR_button2_pixel   (Pixel)   use argument for button2 drawing color
8059 *    isqDR_button2_mode    (int)     tells how to draw; the argument is
8060                               BUTTON2_OPENPOLY   == open polygon
8061                               BUTTON2_CLOSEDPOLY == closed polygon
8062                               BUTTON2_POINTS     == only draw points
8063                               BUTTON2_NODRAW     == don't draw anything
8064 *    isqDR_button2_width   (int) tells width of lines to draw in button2 mode
8065 
8066 *    isqDR_rebar           (ignored) erase the color bar and show it again
8067 
8068 *    isqDR_winfotext       (char *) sets the winfo extra text
8069 
8070 *    isqDR_winfosides      (char **) sets the winfo_sides text
8071 *    isqDR_winfoprefix     (char *) set the winfo_prefix text
8072 
8073 *    isqDR_rinfolabel      (char *) set the rinfo label [11 Mar 2020]
8074 
8075 *    isqDR_getoptions      (ISQ_options *) to get the current options
8076 
8077 *    isqDR_setmontage      (int *) sets the montage parameters
8078                             [0] = nx  [1] = ny  [2] = spacing
8079                             [3] = gap [4] = gap_color (overlay index)
8080 
8081 *    isqDR_setifrac        (float *) sets the image fraction
8082                              between FRAC_MIN and FRAC_MAX
8083 
8084 *    isqDR_opacitybut      (int) turns opacity control on/off
8085 
8086 *    isqDR_setopacity      (int) sets opacity (value=0..9)
8087 *    isqDR_getopacity      (int *) get opacity setting
8088 
8089 *    isqDR_zoombut         (int) turns zoom control on/off
8090 *    isqDR_penbbox         (int) turns pen bbox on/off
8091 
8092 *    isqDR_record_mode     (ignored)
8093                            makes this an image recorder (irreversibly)
8094 
8095 *    isqDR_record_disable  (ignored)
8096                            disables the Rec button (irreversibly)
8097 
8098 *    isqDR_plot_label      (int)
8099                            0..6 for label position; -1=toggle widgets
8100 
8101 *    isqDR_plot_plot       (int)
8102                            1=show overlay plot; 0=don't; -1=toggle widget
8103 
8104 *    isqDR_ignore_redraws  (int)
8105                            1=ignore redraw commands
8106                            0=don't ignore redraw commands
8107 
8108 *    isqDR_setimsave       (char *) suffix of image save mode
8109 
8110 *    isqDR_setrange        (float *) points to rng_bot,rng_top
8111 *    isqDR_settopclip      (float *) points to top_clip
8112 
8113 *    isqDR_keypress        (unsigned int) character or KeySym to send
8114 
8115 *    isqDR_save_jpeg       (char *) save current image to this filename
8116 *    isqDR_save_png        (char *) save current image to this filename
8117 *    isqDR_save_raw        (char *) save current image to this filename
8118 *    isqDR_save_rawmont    (char *) save current montage to this filename
8119 *    isqDR_save_filtered   (char *) save current image to this filter
8120 *    isqDR_save_agif       (char *) save current image series to this filename
8121 *    isqDR_save_mpeg       (char *) save current image series to this filename
8122 *    isqDR_save_jpegall    (char *) save current image series to bunch of files
8123 *    isqDR_save_pngall     (char *) save current image series to bunch of files
8124 
8125 *    isqDR_get_crop        (int *) 4 ints that specify current crop status
8126 *    isqDR_set_crop        (int *) 4 ints to change current crop status
8127 *    isqDR_get_zoom        (int *) 1 int to receive zoom level (1-4)
8128 *    isqDR_set_zoom        (int *) 1 int to set zoom level (1-4)
8129 *    isqDR_get_panoff      (float *) 2 floats = panning offsets
8130 *    isqDR_set_panoff      (float *) 2 floats to set panning offsets
8131 
8132 *    isqDR_allowmerger     (ignored) allows the 3,4,5,6 'merger' buttons
8133 
8134 *    isqDR_pressbut_Colr   (ignored) presses the 'Colr' button
8135 *    isqDR_pressbut_Swap   (ignored) presses the 'Swap' button
8136 *    isqDR_pressbut_Norm   (ignored) presses the 'Norm' button
8137 
8138 The RwcBoolean return value is True for success, False for failure.
8139 -------------------------------------------------------------------------*/
8140 
drive_MCW_imseq(MCW_imseq * seq,int drive_code,XtPointer drive_data)8141 RwcBoolean drive_MCW_imseq( MCW_imseq *seq ,
8142                          int drive_code , XtPointer drive_data )
8143 {
8144 ENTRY("drive_MCW_imseq") ;
8145    if( ! ISQ_VALID(seq) ) RETURN( False );
8146 
8147    switch( drive_code ){
8148 
8149       /*------- error! -------*/
8150 
8151       default:{
8152          fprintf(stderr,"\a\n*** drive_MCW_imseq: code=%d illegal!\n",
8153                  drive_code) ;
8154          /* XBell( seq->dc->display , 100 ) ; */
8155          RETURN( False );
8156       }
8157       break ;
8158 
8159       /*--------- pressbut_Colr [25 Oct 2019] ----------*/
8160 
8161       case isqDR_pressbut_Colr:{
8162         ISQ_but_color_CB( NULL , (XtPointer)seq , NULL ) ;
8163       }
8164       break ;
8165 
8166       /*--------- pressbut_Swap [25 Oct 2019] ----------*/
8167 
8168       case isqDR_pressbut_Swap:{
8169         ISQ_but_cswap_CB( NULL , (XtPointer)seq , NULL ) ;
8170       }
8171       break ;
8172 
8173       /*--------- pressbut_Norm [25 Oct 2019] ----------*/
8174 
8175       case isqDR_pressbut_Norm:{
8176         ISQ_but_cnorm_CB( NULL , (XtPointer)seq , NULL ) ;
8177       }
8178       break ;
8179 
8180       /*--------- set top_clip [14 Sep 2007] ----------*/
8181 
8182       case isqDR_settopclip:{
8183         float *tc=(float *)drive_data ; int zz=0 ;
8184         if( tc == NULL ){
8185           seq->top_clip = 0.0f ; seq->redo_clip = 0 ;
8186         } else {
8187           seq->top_clip = *tc ; zz = (seq->top_clip > 0.0f) ;
8188         }
8189 #if 0
8190 printf("set top_clip=%g  redo_clip=%d zz=%d\n",seq->top_clip,seq->redo_clip,zz);
8191 #endif
8192         ALLOW_CLIPPING( seq , zz ) ;
8193         if( tc == NULL ) ISQ_redisplay( seq , -1 , isqDR_display ) ;
8194         RETURN( True ) ;
8195       }
8196       break ;
8197 
8198       /*--------- save image type? [23 Jan 2003] ----------*/
8199 
8200       case isqDR_setimsave:{
8201         char *suf = (char *)drive_data ;
8202         int ii ;
8203         if( suf == NULL || *suf == '\0' || ppmto_num < 1 ) RETURN(False) ;
8204         for( ii=0 ; ii < ppmto_num ; ii++ ){
8205           if( strcmp(suf  ,ppmto_suffix[ii]) == 0 ) break ;
8206           if( strcmp(suf+1,ppmto_suffix[ii]) == 0 ) break ;
8207         }
8208         if( ii == ppmto_num ) RETURN(False) ;
8209         seq->opt.save_filter = ii ;
8210         SET_SAVE_LABEL(seq) ;
8211         if( seq->num_bbox > 0 && seq->bbox[NTOG_SAV] != NULL )
8212           MCW_set_bbox( seq->bbox[NTOG_SAV] , ppmto_bval[ii] ) ;
8213         RETURN( True ) ;
8214       }
8215       break ;
8216 
8217       /*--------- ignore redraws? [16 Aug 2002] -------------*/
8218 
8219       case isqDR_ignore_redraws:{
8220          int dd = PTOI(drive_data) ;
8221          seq->ignore_redraws = dd ;
8222          RETURN( True ) ;
8223       }
8224       break ;
8225 
8226       /*--------- allowmerger [25 Aug 2014] ----------*/
8227 
8228       case isqDR_allowmerger:{
8229         seq->allowmerger = 1 ;
8230         RETURN( True ) ;
8231       }
8232       break ;
8233 
8234       /*--------- set display range [04 Nov 2003] ----------*/
8235 
8236       case isqDR_setrange:{
8237         float *rng = (float *)drive_data ;
8238         if( rng == NULL ){
8239           seq->rng_bot = seq->rng_top = seq->rng_ztop = 0.0f ;
8240         } else {
8241           seq->rng_bot = rng[0] ; seq->rng_top = rng[1] ; seq->rng_ztop = 0.0 ;
8242           seq->rng_extern = 1 ;
8243         }
8244         if( rng == NULL || rng[2] == 0.0f )
8245           ISQ_redisplay( seq , -1 , isqDR_display ) ;
8246         RETURN( True ) ;
8247       }
8248       break ;
8249 
8250       /*--------- overlay plot stuff [20 Sep 2001] ----------*/
8251 
8252       case isqDR_plot_label:{
8253          int dd = PTOI(drive_data) ;
8254 
8255          if( dd < 0 ){
8256             INVERT_manage( seq->wbar_label_av->wrowcol ) ;
8257             INVERT_manage( seq->wbar_labsz_av->wrowcol ) ;
8258          } else if( dd != seq->wbar_label_av->ival && dd >= 0 && dd <= 5 ){
8259            AV_assign_ival( seq->wbar_label_av , dd ) ;
8260            ISQ_redisplay( seq , -1 , isqDR_display ) ;
8261          }
8262          RETURN( True ) ;
8263       }
8264 
8265       /*.....................................................*/
8266 
8267       case isqDR_save_jpeg:{                 /* 28 Jul 2005 */
8268         char *fname = (char *)drive_data ;
8269         ISQ_save_jpeg( seq , fname ) ;
8270         RETURN( True ) ;
8271       }
8272 
8273       case isqDR_save_png:{                  /* 11 Dec 2006 */
8274         char *fname = (char *)drive_data ;
8275         ISQ_save_png( seq , fname ) ;
8276         RETURN( True ) ;
8277       }
8278 
8279       case isqDR_save_raw:{                  /* 13 Nov 2007 */
8280         char *fname = (char *)drive_data ;
8281         ISQ_save_raw( seq , fname ) ;
8282         RETURN( True ) ;
8283       }
8284 
8285       case isqDR_save_rawmont:{              /* 13 Nov 2007 */
8286         char *fname = (char *)drive_data ;
8287         ISQ_save_rawmont( seq , fname ) ;
8288         RETURN( True ) ;
8289       }
8290 
8291       case isqDR_save_filtered:{             /* 14 Dec 2006 */
8292         char *fname = (char *)drive_data ;
8293         ISQ_save_image( seq , NULL , fname , NULL ) ;
8294         RETURN( True ) ;
8295       }
8296 
8297       case isqDR_save_agif:{
8298         char *fname = (char *)drive_data ;
8299         ISQ_save_anim( seq , fname , 0,0, AGIF_MODE ) ;
8300         RETURN(True) ;
8301       }
8302 
8303       case isqDR_save_mpeg:{
8304         char *fname = (char *)drive_data ;
8305         ISQ_save_anim( seq , fname , 0,0, MPEG_MODE ) ;
8306         RETURN(True) ;
8307       }
8308 
8309       case isqDR_save_jpegall:{
8310         char *fname = (char *)drive_data ;
8311         ISQ_save_anim( seq , fname , 0,0, JPEG_MODE ) ;
8312         RETURN(True) ;
8313       }
8314 
8315       case isqDR_save_pngall:{
8316         char *fname = (char *)drive_data ;
8317         ISQ_save_anim( seq , fname , 0,0, PNG_MODE ) ;
8318         RETURN(True) ;
8319       }
8320 
8321       /*.....................................................*/
8322 
8323       case isqDR_plot_plot:{
8324          int dd = PTOI(drive_data) ;
8325 
8326          if( dd < 0 ){
8327             INVERT_manage( seq->wbar_plots_bbox->wrowcol ) ;
8328          } else {
8329             dd = (dd != 0) ;
8330             if( dd != MCW_val_bbox(seq->wbar_plots_bbox) ){
8331                MCW_set_bbox( seq->wbar_plots_bbox , dd ) ;
8332                ISQ_redisplay( seq , -1 , isqDR_display ) ;
8333             }
8334          }
8335          RETURN( True ) ;
8336       }
8337 
8338       /*--------- record off forever [24 Apr 2001] ----------*/
8339 
8340       case isqDR_record_disable:{
8341          ISQ_remove_widget( seq , seq->record_rc ) ;
8342          seq->record_status = RECORD_STATUS_OFF ;
8343          RETURN( True ) ;
8344       }
8345       break ;
8346 
8347       /*--------- record mode [24 Apr 2001] ----------*/
8348 
8349       case isqDR_record_mode:{
8350          int ii ;
8351          static Pixmap record_pixmap = XmUNSPECIFIED_PIXMAP ;
8352 #define record_width 64
8353 #define record_height 32
8354 static unsigned char record_bits[] = {
8355    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00,
8356    0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8357    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x0f, 0x00, 0x00,
8358    0x00, 0x00, 0x00, 0x00, 0x81, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8359    0x81, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x40, 0x00, 0x00,
8360    0x00, 0x00, 0x00, 0x02, 0x81, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
8361    0x81, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x81, 0x40, 0x00, 0x00,
8362    0x00, 0x00, 0x00, 0x02, 0x81, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
8363    0x81, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x81, 0x10, 0x00, 0x00,
8364    0x00, 0x00, 0x00, 0x02, 0x81, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
8365    0x81, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x81, 0x10, 0x00, 0x00,
8366    0x00, 0x00, 0x00, 0x02, 0x81, 0x10, 0x70, 0x00, 0xe0, 0xf0, 0x01, 0x02,
8367    0x81, 0x20, 0x8c, 0xf0, 0x10, 0x21, 0xe2, 0x03, 0x81, 0x20, 0x02, 0x09,
8368    0x09, 0x22, 0x12, 0x02, 0x81, 0x20, 0x02, 0x09, 0x08, 0x22, 0x10, 0x02,
8369    0x81, 0x20, 0xfe, 0x09, 0x08, 0x22, 0x10, 0x02, 0x81, 0x40, 0x02, 0x08,
8370    0x08, 0x22, 0x10, 0x02, 0x81, 0x40, 0x02, 0x08, 0x08, 0x22, 0x10, 0x02,
8371    0x81, 0x40, 0x02, 0x08, 0x08, 0x22, 0x10, 0x02, 0x81, 0x40, 0x02, 0x08,
8372    0x08, 0x22, 0x10, 0x02, 0x81, 0x40, 0x04, 0x11, 0x11, 0x21, 0x20, 0x02,
8373    0x81, 0x40, 0xf8, 0xe0, 0xe0, 0x20, 0xc0, 0x01, 0x01, 0x00, 0x00, 0x00,
8374    0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8375    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8376    0x00, 0x00, 0x00, 0x00};
8377 
8378          if( seq->record_mode ) RETURN( False ) ;  /* already on */
8379          seq->record_mode = 1 ;
8380          seq->cropit = seq->crop_allowed = 0 ;     /* 12 Jun 2002 */
8381 
8382          /* create background pixmap */
8383 
8384          if( record_pixmap == XmUNSPECIFIED_PIXMAP )
8385             record_pixmap = XCreatePixmapFromBitmapData(
8386                               seq->dc->display ,
8387                               RootWindowOfScreen(seq->dc->screen) ,
8388                               (char *)record_bits, record_width, record_height ,
8389                               seq->dc->ovc->pixov_brightest ,
8390                               seq->dc->ovc->pixov_darkest ,
8391                               DefaultDepthOfScreen(seq->dc->screen) ) ;
8392 
8393          XtVaSetValues( seq->wform, XmNbackgroundPixmap, record_pixmap, NULL ) ;
8394 
8395          /* disable various widgets */
8396 
8397          ISQ_remove_widget( seq , seq->wbut_bot[NBUT_MONT] ) ;
8398          ISQ_remove_widget( seq , seq->record_rc ) ;
8399          for( ii=0 ; ii < NBUTTON_RIG ; ii++)
8400             ISQ_remove_widget( seq , seq->wbut_rig[ii] ) ;
8401          for( ii=0 ; ii < NARROW-1 ; ii++ ) /* keep "i" arrow */
8402             ISQ_remove_widget( seq , seq->arrow[ii]->wrowcol ) ;
8403          if( seq->ov_opacity_av != NULL ){
8404             ISQ_remove_widget( seq , seq->ov_opacity_sep ) ;
8405             ISQ_remove_widget( seq , seq->ov_opacity_av->wrowcol ) ;
8406          }
8407 
8408          ISQ_remove_widget( seq , seq->zoom_sep ) ;
8409          ISQ_remove_widget( seq , seq->zoom_val_av->wrowcol ) ;
8410          ISQ_remove_widget( seq , seq->zoom_drag_pb ) ;
8411          ISQ_remove_widget( seq , seq->crop_drag_pb ) ;
8412          ISQ_remove_widget( seq , seq->pen_bbox->wrowcol ) ;
8413 
8414          ISQ_remove_widget( seq , seq->arrowpad->wform ) ;
8415          ISQ_remove_widget( seq , seq->wbar ) ;
8416          ISQ_remove_widget( seq , seq->winfo ) ;
8417          ISQ_remove_widget( seq , seq->rinfo ) ;
8418 
8419          /* change to Save:bkg */
8420 
8421          seq->opt.save_one    = 0 ;
8422          seq->opt.save_agif   = 0 ;   /* 27 Jul 2001 */
8423          seq->opt.save_mpeg   = 0 ;
8424          seq->opt.save_pnm    = 0 ;
8425          seq->opt.save_filter = -1 ;  /* 27 Jun 2001 */
8426          SET_SAVE_LABEL(seq) ;
8427          drive_MCW_imseq( seq , isqDR_setimsave ,
8428                           (XtPointer)getenv("AFNI_DEFAULT_IMSAVE") ) ;
8429 
8430          /* 27 Jun 2001: change help on Save: */
8431 
8432          MCW_unregister_help( seq->wbut_bot[NBUT_DISP] ) ;
8433          if( ppmto_num > 0 ){
8434            MCW_register_help( seq->wbut_bot[NBUT_SAVE] ,
8435                                 "Save controls:\n"
8436                                 " Press with Button 1 (left) to save images\n"
8437                                 " Press with Button 3 (right) to change the\n"
8438                                 "   format of the saved images"
8439                             ) ;
8440            MCW_register_hint( seq->wbut_bot[NBUT_SAVE] ,
8441                               "Button 3 => change save format" ) ;
8442          } else {
8443            MCW_register_help( seq->wbut_bot[NBUT_SAVE] ,
8444                                 "Save controls:\n"
8445                                 " Press with Button 1 (left) to\n"
8446                                 " in the PNM format save images"  ) ;
8447            MCW_register_hint( seq->wbut_bot[NBUT_SAVE] ,
8448                               "Save images as PNM" ) ;
8449          }
8450 
8451          /* change Disp to Kill */
8452 
8453          XtRemoveCallback( seq->wbut_bot[NBUT_DISP] , XmNactivateCallback ,
8454                            ISQ_but_bot_def[NBUT_DISP].func_CB , seq        ) ;
8455 
8456          XtAddCallback( seq->wbut_bot[NBUT_DISP] , XmNactivateCallback ,
8457                         ISQ_record_kill_CB , seq                       ) ;
8458 
8459          MCW_set_widget_label( seq->wbut_bot[NBUT_DISP] , "Kill" ) ;
8460          MCW_unregister_help( seq->wbut_bot[NBUT_DISP] ) ;
8461          MCW_register_hint( seq->wbut_bot[NBUT_DISP] , "Erase current image" ) ;
8462          MCW_register_help( seq->wbut_bot[NBUT_DISP] ,
8463                             "Erase the current image in the recorded sequence.\n"
8464                             "If not later overwritten, this image will NOT\n"
8465                             "be saved when you use Save:bkg to write the image\n"
8466                             "sequence to disk.\n"
8467                            ) ;
8468 
8469          /* attach Done to Save (since Mont is now hidden) */
8470 
8471          XtVaSetValues( seq->wbut_bot[NBUT_DONE] ,
8472                            LEADING_BOT       , XmATTACH_WIDGET          ,
8473                            LEADING_WIDGET_BOT, seq->wbut_bot[NBUT_SAVE] ,
8474                         NULL ) ;
8475 
8476          /* Miscellaneous stuff */
8477 
8478          XtVaSetValues( seq->wtop , XmNtitle , "Image Recorder" , NULL ) ;
8479          if( MCW_isitmwm( seq->wtop ) )
8480             XtVaSetValues( seq->wtop ,
8481                             XmNmwmDecorations, MWM_DECOR_ALL | MWM_DECOR_MAXIMIZE,
8482                            NULL ) ;
8483 
8484          RETURN( True ) ;
8485       }
8486       break ;
8487 
8488       /*--------- opacity button [07 Mar 2001] ----------*/
8489 
8490       case isqDR_opacitybut:{
8491          int val = PTOI(drive_data) ;
8492          if( seq->ov_opacity_av == NULL ) RETURN( False ) ;
8493          if( val == 0 ){
8494             XtUnmanageChild( seq->ov_opacity_sep ) ;
8495             XtUnmanageChild( seq->ov_opacity_av->wrowcol ) ;
8496          }
8497          else {
8498             XtManageChild( seq->ov_opacity_sep ) ;
8499             XtManageChild( seq->ov_opacity_av->wrowcol ) ;
8500          }
8501          RETURN( True ) ;
8502       }
8503       break ;
8504 
8505       /*--------- set opacity value [21 Jan 2003] ----------*/
8506 
8507       case isqDR_setopacity:{
8508         int val = PTOI(drive_data) ;
8509         if( seq->ov_opacity_av == NULL ) RETURN( False ) ;
8510         if( val < OPACITY_BOT || val > OPACITY_TOP ) RETURN( False ) ;
8511         if( val == seq->ov_opacity_av->ival ) RETURN( True ) ;
8512         AV_assign_ival( seq->ov_opacity_av , val ) ;
8513         ISQ_opacity_CB( seq->ov_opacity_av , seq ) ;
8514         RETURN( True ) ;
8515       }
8516       break ;
8517 
8518       /*--------- get opacity value [21 Jan 2003] ----------*/
8519 
8520       case isqDR_getopacity:{
8521         int *val = (int *) drive_data ;
8522         if( seq->ov_opacity_av == NULL || val == NULL ) RETURN( False ) ;
8523         *val = seq->ov_opacity_av->ival ;
8524         RETURN( True ) ;
8525       }
8526       break ;
8527 
8528       /*--------- get zoom data [10 Dec 2019] -----------*/
8529 
8530       case isqDR_get_zoom:{
8531         int *iar = (int *)drive_data ;
8532         if( iar != NULL ){
8533           iar[0] = seq->zoom_fac ;
8534         }
8535         RETURN(True) ;
8536       }
8537       break ;
8538 
8539       /*--------- set zoom data [10 Dec 2019] -----------*/
8540 
8541       case isqDR_set_zoom:{
8542         int *val = (int *)drive_data ;
8543         if( val != NULL && *val >= ZOOM_BOT && *val <= ZOOM_TOP      &&
8544                                                *val != seq->zoom_fac   ){
8545           AV_assign_ival( seq->zoom_val_av , *val ) ;
8546           ISQ_zoom_av_CB( seq->zoom_val_av , seq ) ;
8547         }
8548         RETURN(True) ;
8549       }
8550       break ;
8551 
8552       /*--------- set the panning offsets [10 Dec 2019] ----------*/
8553 
8554       case isqDR_set_panoff:{
8555         float *val = (float *)drive_data , zlev = seq->zoom_fac ;;
8556         if( val != NULL && zlev > 1.0f ){
8557           float hoff = val[0] , voff = val[1] , mh = (zlev-1.001f)/zlev ;
8558                if( hoff > mh  ) hoff = mh  ;
8559           else if( hoff < 0.0 ) hoff = 0.0 ;
8560                if( voff > mh  ) voff = mh  ;
8561           else if( voff < 0.0 ) voff = 0.0 ;
8562           if( hoff != seq->zoom_hor_off || voff != seq->zoom_ver_off ){
8563             seq->zoom_hor_off = hoff ;
8564             seq->zoom_ver_off = voff ;
8565             ISQ_show_zoom( seq ) ;  /* no visible panning widgets to alter */
8566             ZOOM_CHANGE(seq) ;
8567           }
8568         }
8569         RETURN(True) ;
8570       }
8571       break ;
8572 
8573       /*--------- get the panning offsets [10 Dec 2019] ----------*/
8574 
8575       case isqDR_get_panoff:{
8576         float *val = (float *)drive_data , zlev = seq->zoom_fac ;
8577         if( val != NULL ){
8578           if( zlev > 1.0f ){
8579             val[0] = seq->zoom_hor_off ;
8580             val[1] = seq->zoom_ver_off ;
8581           } else {
8582             val[0] = val[1] = 0.0f ;
8583           }
8584         }
8585         RETURN(True) ;
8586       }
8587       break ;
8588 
8589       /*--------- get crop data [03 May 2007] -----------*/
8590 
8591       case isqDR_get_crop:{
8592         int *iar = (int *)drive_data ;
8593         if( iar != NULL ){
8594           if( !seq->cropit ){
8595             iar[0] = iar[1] = iar[2] = iar[3] = -1 ;
8596           } else {
8597             iar[0] = seq->crop_xa ; iar[1] = seq->crop_xb ;
8598             iar[2] = seq->crop_ya ; iar[3] = seq->crop_yb ;
8599           }
8600         }
8601         RETURN(True) ;
8602       }
8603       break ;
8604 
8605       /*--------- set crop data [03 May 2007] -----------*/
8606 
8607       case isqDR_set_crop:{
8608         int *iar = (int *)drive_data ;
8609         if( iar == NULL              ||
8610             iar[0] < 0               || iar[2] < 0               ||
8611             iar[0]+MINCROP >= iar[1] || iar[2]+MINCROP >= iar[3]   ){
8612           seq->cropit = 0 ;
8613         } else {
8614           seq->cropit = 1 ;
8615           seq->crop_xa = iar[0] ; seq->crop_xb = iar[1] ;
8616           seq->crop_ya = iar[2] ; seq->crop_yb = iar[3] ;
8617           if( seq->crop_nxorg > 0 && seq->crop_xb >= seq->crop_nxorg )
8618             seq->crop_xb = seq->crop_nxorg-1 ;
8619           if( seq->crop_nyorg > 0 && seq->crop_yb >= seq->crop_nyorg )
8620             seq->crop_yb = seq->crop_nyorg-1 ;
8621         }
8622         ISQ_redisplay( seq , -1 , isqDR_display ) ;
8623         RETURN(True) ;
8624       }
8625       break ;
8626 
8627       /*--------- zoom buttons [11 Mar 2002] ----------*/
8628 
8629       case isqDR_zoombut:{
8630          int val = PTOI(drive_data) ;
8631          if( val == 0 ){
8632             XtUnmanageChild( seq->zoom_sep ) ;
8633             XtUnmanageChild( seq->zoom_val_av->wrowcol ) ;
8634             XtUnmanageChild( seq->zoom_drag_pb ) ;
8635             XtUnmanageChild( seq->crop_drag_pb ) ;
8636          } else {
8637             XtManageChild( seq->zoom_sep ) ;
8638             XtManageChild( seq->zoom_val_av->wrowcol ) ;
8639             XtManageChild( seq->zoom_drag_pb ) ;
8640             XtManageChild( seq->crop_drag_pb ) ;
8641          }
8642          RETURN( True ) ;
8643       }
8644       break ;
8645 
8646       /*--------- pen bbox [18 Jul 2003] ----------*/
8647 
8648       case isqDR_penbbox:{
8649          int val = PTOI(drive_data) ;
8650          if( val == 0 )
8651            XtUnmanageChild( seq->pen_bbox->wrowcol ) ;
8652          else
8653            XtManageChild( seq->pen_bbox->wrowcol ) ;
8654          RETURN( True ) ;
8655       }
8656       break ;
8657 
8658       /*--------- set montage [22 Sep 2000] ----------*/
8659       /* [mostly copied from ISQ_montage_action_CB()] */
8660 
8661       case isqDR_setmontage:{
8662          int *mm = (int *) drive_data ;
8663 
8664          if( mm == NULL )                     RETURN( False );  /* sanity */
8665          if( mm[0] < 1 || mm[0] > MONT_NMAX ) RETURN( False );  /* checks */
8666          if( mm[1] < 1 || mm[1] > MONT_NMAX ) RETURN( False );
8667 
8668          seq->mont_nx_old       = seq->mont_nx       ;  /* save */
8669          seq->mont_ny_old       = seq->mont_ny       ;
8670          seq->mont_skip_old     = seq->mont_skip     ;
8671          seq->mont_gap_old      = seq->mont_gap      ;
8672          seq->mont_gapcolor_old = seq->mont_gapcolor ;
8673 
8674          /* set new values, if legal */
8675 
8676          seq->mont_nx = mm[0] ;
8677          seq->mont_ny = mm[1] ;
8678          if( mm[2] >  0 && mm[2] <= MONT_SMAX ) seq->mont_skip     = mm[2]-1 ;
8679          if( mm[3] >= 0 && mm[3] <= MONT_GMAX ) seq->mont_gap      = mm[3] ;
8680          if( mm[4] >= 0 &&
8681              mm[4] <= seq->dc->ovc->ncol_ov-1 ) seq->mont_gapcolor = mm[4] ;
8682 
8683          /* set Save One */
8684 
8685          if( seq->mont_nx * seq->mont_ny > 1 && !seq->opt.save_one ){
8686             seq->opt.save_one  = 1 ;
8687             seq->opt.save_agif = 0 ; /* 27 Jul 2001 */
8688             seq->opt.save_mpeg = 0 ;
8689             SET_SAVE_LABEL(seq) ;
8690          }
8691 
8692          /* now do the redisplay */
8693 
8694          ISQ_redisplay( seq , -1 , isqDR_display ) ;    /* local redraw */
8695 
8696          if( seq->status->send_CB != NULL ){  /* tell AFNI */
8697 
8698             ISQ_cbs cbs ;
8699             THD_ivec3 minf ;
8700             int ijcen = (seq->mont_nx)/2 + (seq->mont_ny/2) * seq->mont_nx ,
8701                 nmont = seq->mont_nx * seq->mont_ny ;
8702 
8703             minf.ijk[0]  = ijcen ;            /* number of slices before center */
8704             minf.ijk[1]  = nmont-ijcen-1 ;    /* number after */
8705             minf.ijk[2]  = seq->mont_skip ;   /* number between slices */
8706             cbs.reason   = isqCR_newmontage ;
8707             cbs.userdata = (XtPointer) &minf ;
8708 
8709             seq->ignore_redraws = 1 ;         /* don't listen to redraws */
8710 #if 0
8711             seq->status->send_CB( seq , seq->getaux , &cbs ) ;
8712 #else
8713             SEND(seq,cbs) ;
8714 #endif
8715             seq->ignore_redraws = 0 ;         /* can listen again */
8716          }
8717 
8718 #if 0
8719          ISQ_redisplay( seq , -1 , isqDR_display ) ;    /* local redraw */
8720 #endif
8721 
8722          RETURN( True );
8723       }
8724       break ;
8725 
8726       /*------- winfo_prefix text [10 Dec 2007] -------*/
8727 
8728       case isqDR_winfoprefix:{
8729         char *pf=(char *)drive_data , lab[2]=" " ;
8730         if( pf == NULL || *pf == '\0' ){
8731           seq->winfo_prefix[0] = '\0' ;
8732         } else {
8733           strncpy( seq->winfo_prefix , pf , 15 ) ;
8734           seq->winfo_prefix[15] = '\0' ;
8735           if( isgraph(*pf) ) lab[0]=*pf ;
8736         }
8737         MCW_set_widget_label( seq->arrowpad->wbut[4] , lab ) ;
8738         seq->im_label[0] = '\0' ;  /* will force redraw */
8739         ISQ_draw_winfo( seq ) ;
8740         RETURN( True );
8741       }
8742 
8743       /*------- rinfo label text [11 Mar 2020] -------*/
8744 
8745       case isqDR_rinfolabel:{
8746         char *pf=(char *)drive_data  ;
8747         if( pf == NULL || *pf == '\0' ) pf = "OK" ;
8748         strncpy( seq->rinfo_label , pf , 6 ) ;
8749         seq->rinfo_label[6] = '\0' ;
8750         MCW_set_widget_label( seq->rinfo , seq->rinfo_label ) ;
8751         RETURN( True );
8752       }
8753 
8754       /*------- winfo_sides text [01 Dec 1999] -------*/
8755 
8756       case isqDR_winfosides:{
8757          char **ws = (char **) drive_data ;
8758          int iw ;
8759 
8760          if( ws == NULL ){                   /* remove the label data */
8761            seq->winfo_sides[0][0] =
8762             seq->winfo_sides[1][0] =
8763              seq->winfo_sides[2][0] =
8764               seq->winfo_sides[3][0] = '\0' ;
8765 
8766          } else {                           /* change the label data */
8767            for( iw=0 ; iw < 4 ; iw++ ){
8768              if( ws[iw] == NULL || ws[iw][0] == '\0' ){
8769                seq->winfo_sides[iw][0] = '\0' ;
8770              } else {
8771                strncpy( seq->winfo_sides[iw] , ws[iw] , 15 ) ;
8772                seq->winfo_sides[iw][15] = '\0' ;
8773              }
8774            }
8775          }
8776          seq->im_label[0] = '\0' ;  /* will force redraw */
8777          ISQ_draw_winfo( seq ) ;
8778 
8779          if( ws == NULL )                       /* 18 May 2005: add a hint */
8780            MCW_unregister_hint( seq->winfo ) ;  /* for the clueless newbie */
8781          else
8782            MCW_register_hint( seq->winfo ,
8783                     "setenv AFNI_LEFT_IS_LEFT YES disables 'radiology mode'" );
8784 
8785          RETURN( True );
8786       }
8787 
8788       /*------- setifrac [22 Sep 2000] -------*/
8789       /* [mostly copied from ISQ_arrow_CB()]  */
8790 
8791       case isqDR_setifrac:{
8792          float *ff = (float *) drive_data ;
8793 
8794          if( ff == NULL || *ff < FRAC_MIN || *ff > 1.0 ) RETURN( False );
8795 
8796          if( *ff <= FRAC_MAX ){ /* from ISQ_arrow_CB() */
8797             float nfrac = *ff ;
8798             seq->image_frac = nfrac ;
8799 
8800             if( !seq->onoff_state )  /* turn widgets on first, recursively */
8801                drive_MCW_imseq( seq,isqDR_onoffwid,(XtPointer)isqDR_onwid );
8802 
8803             XtVaSetValues( seq->wimage ,
8804                              XmNrightPosition ,(int)(0.49+nfrac*FORM_FRAC_BASE),
8805                              XmNbottomPosition,(int)(0.49+nfrac*FORM_FRAC_BASE),
8806                            NULL ) ;
8807             XtVaSetValues( seq->wscale ,
8808                              XmNrightPosition ,(int)(0.49+nfrac*FORM_FRAC_BASE),
8809                            NULL ) ;
8810             XtVaSetValues( seq->wbar ,
8811                              XmNbottomPosition,(int)(0.49+nfrac*FORM_FRAC_BASE),
8812                            NULL ) ;
8813             XtVaSetValues( seq->winfo ,
8814                              XmNrightPosition ,(int)(0.49+nfrac*FORM_FRAC_BASE),
8815                            NULL ) ;
8816 
8817          } else if( seq->onoff_state ) {  /* turn widgets off */
8818 
8819             drive_MCW_imseq( seq,isqDR_onoffwid,(XtPointer)isqDR_offwid );
8820 
8821          }
8822          RETURN( True );
8823       }
8824       break ;
8825 
8826       /*------- send a simulated key press [18 Feb 2005] -------*/
8827 
8828       case isqDR_keypress:{
8829         unsigned int key = (unsigned int)PTOI(drive_data) ;
8830         (void )ISQ_handle_keypress( seq , key , 0 ) ;
8831         RETURN( True );
8832       }
8833       break ;
8834 
8835       /*------- winfo extra text [07 Aug 1999] -------*/
8836 
8837       case isqDR_winfotext:{
8838          char *wt = (char *) drive_data ;
8839 
8840          if( wt == NULL || wt[0] == '\0' ){
8841             seq->winfo_extra[0] = '\0' ;
8842          } else {
8843             strncpy( seq->winfo_extra , wt , 63 ) ;
8844             seq->winfo_extra[63] = '\0' ;
8845          }
8846          seq->im_label[0] = '\0' ;  /* will force redraw */
8847          ISQ_draw_winfo( seq ) ;
8848          RETURN( True );
8849       }
8850 
8851       /*------- button2 stuff -------*/
8852 
8853       case isqDR_button2_pixel:{
8854          seq->button2_pixel = (Pixel) drive_data ;
8855          RETURN( True );
8856       }
8857 
8858       case isqDR_button2_mode:{
8859          seq->button2_drawmode = PTOI(drive_data) ;
8860          RETURN( True );
8861       }
8862 
8863       case isqDR_button2_width:{                  /* 08 Oct 2002 */
8864          seq->button2_width = PTOI(drive_data) ;
8865          RETURN( True );
8866       }
8867 
8868       case isqDR_button2_enable:{
8869          ISQ_timer_stop(seq) ;
8870          if( seq->status->send_CB == NULL ) RETURN( False );  /* makes no sense */
8871          if( seq->button2_enabled )         RETURN( True );   /* already on */
8872 
8873          XtInsertEventHandler(
8874               seq->wimage ,         /* handle events in image */
8875 
8876                0
8877                | ButtonReleaseMask  /* button releases (only #2 is used) */
8878                | Button2MotionMask  /* motion while #2 is down */
8879               ,
8880               FALSE ,               /* nonmaskable events? */
8881               ISQ_button2_EV ,      /* handler routine */
8882               (XtPointer) seq ,     /* client data */
8883               XtListTail            /* last in queue */
8884          ) ;
8885 
8886          seq->button2_enabled = 1 ;
8887          seq->button2_active  = 0 ;
8888 
8889          XtManageChild( seq->pen_bbox->wrowcol ) ;
8890          RETURN( True );
8891       }
8892 
8893       case isqDR_button2_disable:{
8894          if( seq->status->send_CB == NULL ) RETURN( False );  /* makes no sense */
8895          if( !seq->button2_enabled )        RETURN( True );   /* already off */
8896 
8897          XtRemoveEventHandler(
8898               seq->wimage ,         /* unhandle events in image */
8899 
8900                0
8901                | ButtonReleaseMask  /* button releases (only #2 is used) */
8902                | Button2MotionMask  /* motion while #2 is down */
8903               ,
8904               TRUE ,                /* nonmaskable events? */
8905               ISQ_button2_EV ,      /* handler routine */
8906               (XtPointer) seq       /* client data */
8907          ) ;
8908 
8909          seq->button2_enabled = seq->button2_active = 0 ;
8910          ISQ_set_cursor_state( seq , CURSOR_NORMAL ) ;
8911          XtUnmanageChild( seq->pen_bbox->wrowcol ) ;
8912          RETURN( True );
8913       }
8914 
8915       /*------- montage stuff -------*/
8916 
8917       case isqDR_periodicmont:{
8918         int per = (PTOI(drive_data)) != 0 ;
8919 
8920         if( per != seq->mont_periodic ){
8921            seq->mont_periodic = per ;
8922            if( ISQ_REALZ(seq) ) ISQ_redisplay( seq , -1 , isqDR_display ) ;
8923         }
8924         RETURN( True );
8925       }
8926 
8927       case isqDR_montmode:{
8928         int mmm = (PTOI(drive_data)) != 0 ;
8929 
8930         if( mmm != seq->mont_mode ){
8931            seq->mont_mode = mmm ;
8932            if( ISQ_REALZ(seq) ) ISQ_redisplay( seq , -1 , isqDR_display ) ;
8933         }
8934         RETURN( True );
8935       }
8936 
8937       case isqDR_sendmontage:{
8938          if( seq->status->send_CB != NULL ){
8939             ISQ_cbs cbs ;
8940             THD_ivec3 minf ;
8941             int ijcen = (seq->mont_nx)/2 + (seq->mont_ny/2) * seq->mont_nx ,
8942                 nmont = seq->mont_nx * seq->mont_ny ;
8943 
8944             minf.ijk[0]  = ijcen ;            /* number of slices before center */
8945             minf.ijk[1]  = nmont-ijcen-1 ;    /* number after */
8946             minf.ijk[2]  = seq->mont_skip ;   /* number between slices */
8947             cbs.reason   = isqCR_newmontage ;
8948             cbs.userdata = (XtPointer) &minf ;
8949 #if 0
8950             seq->status->send_CB( seq , seq->getaux , &cbs ) ;
8951 #else
8952             SEND(seq,cbs) ;
8953 #endif
8954             RETURN( True );
8955          } else {
8956             RETURN( False );
8957          }
8958       }
8959       break ;
8960 
8961       /*------ set icon -----*/
8962 
8963       case isqDR_icon:{
8964          XtVaSetValues( seq->wtop, XmNiconPixmap,(Pixmap)drive_data , NULL ) ;
8965          RETURN( True );
8966       }
8967       break ;
8968 
8969       /*------ set background icon [28 Jan 2004] -------*/
8970 
8971       case isqDR_bgicon:{
8972         XtVaSetValues( seq->wform,
8973                          XmNbackgroundPixmap, (Pixmap)drive_data ,
8974                        NULL ) ;
8975         RETURN( True );
8976       }
8977       break ;
8978 
8979       /*------ get image number -----*/
8980 
8981       case isqDR_getimnr:{
8982          int *retval = (int *) drive_data ;
8983 
8984          if( retval != NULL ) *retval = seq->im_nr ;
8985          RETURN( True );
8986       }
8987       break ;
8988 
8989       /*------ widgets on or off -----*/
8990 
8991       case isqDR_onoffwid:{
8992          int mode = PTOI(drive_data) , turn_on ;
8993          int ww , hh ;
8994 
8995          switch( mode ){
8996             default:
8997             case isqDR_togwid:  turn_on = ! seq->onoff_state ; break ;
8998             case isqDR_onwid:   turn_on = 1                  ; break ;
8999             case isqDR_offwid:  turn_on = 0                  ; break ;
9000          }
9001 
9002          if( turn_on == seq->onoff_state ) RETURN( True );
9003 
9004          MCW_widget_geom( seq->wimage , &ww , &hh , NULL,NULL ) ;
9005 
9006          if( turn_on ){
9007             MCW_manage_widgets( seq->onoff_widgets , seq->onoff_num ) ;
9008             XtVaSetValues(
9009                seq->wimage ,
9010                   XmNrightPosition ,(int)( 0.49+seq->image_frac*FORM_FRAC_BASE ),
9011                   XmNbottomPosition,(int)( 0.49+seq->image_frac*FORM_FRAC_BASE ),
9012                NULL ) ;
9013             XtVaSetValues( seq->wtop ,
9014                               XmNwidth  , (int)(0.49+ww/seq->image_frac) ,
9015                               XmNheight , (int)(0.49+hh/seq->image_frac) ,
9016                            NULL ) ;
9017             if( !seq->button2_enabled ) XtUnmanageChild(seq->pen_bbox->wrowcol);
9018          } else {
9019             MCW_unmanage_widgets( seq->onoff_widgets , seq->onoff_num ) ;
9020             XtVaSetValues( seq->wimage ,
9021                               XmNrightPosition , FORM_FRAC_BASE ,
9022                               XmNbottomPosition, FORM_FRAC_BASE ,
9023                            NULL ) ;
9024             XtVaSetValues( seq->wtop ,
9025                               XmNwidth  , ww ,
9026                               XmNheight , hh ,
9027                            NULL ) ;
9028          }
9029 
9030          seq->onoff_state = turn_on ;
9031          RETURN( True );
9032       }
9033       break ;
9034 
9035       /*------- title --------*/
9036 
9037       case isqDR_title:{
9038          char *title = (char *) drive_data ;
9039 
9040          if( title == NULL || strlen(title) == 0 ) title = "AFNI" ;
9041 
9042          XtVaSetValues( seq->wtop , XmNtitle , title , NULL ) ;
9043 #if 1
9044          if( MCW_isitmwm( seq->wtop ) )
9045             XtVaSetValues( seq->wtop ,
9046                             XmNmwmDecorations, MWM_DECOR_ALL | MWM_DECOR_MAXIMIZE,
9047                            NULL ) ;
9048 #endif
9049          RETURN( True );
9050       }
9051       break ;
9052 
9053       /*------- death! -------*/
9054 
9055       case isqDR_destroy:{
9056          ISQ_timer_stop(seq) ; NI_sleep(1) ;
9057          ISQ_but_done_CB( NULL , (XtPointer)seq , NULL ) ; NI_sleep(1) ;
9058          RETURN( True );
9059       }
9060       break ;
9061 
9062       /*------- unrealize! -------*/
9063 
9064       case isqDR_unrealize:{
9065          ISQ_timer_stop(seq) ;
9066          if( ISQ_REALZ(seq) ){ XtUnrealizeWidget(seq->wtop); NI_sleep(1); }
9067          seq->valid = 1 ;
9068          RETURN( True );
9069       }
9070       break ;
9071 
9072       /*------- realize! -------*/
9073 
9074       case isqDR_realize:{
9075          if( ! ISQ_REALZ(seq) ){
9076             XtRealizeWidget( seq->wtop )   ; NI_sleep(1) ;
9077             WAIT_for_window( seq->wtop )   ;
9078             NORMAL_cursorize( seq->wtop )  ;
9079             POPUP_cursorize( seq->wimage ) ;
9080             POPUP_cursorize( seq->wbar )   ;
9081             POPUP_cursorize( seq->wbut_bot[NBUT_SAVE] ) ;
9082             POPUP_cursorize( seq->crop_drag_pb ) ;
9083             XmUpdateDisplay( seq->wtop )   ;
9084          }
9085 #ifndef DONT_ONOFF_ONE
9086          if( seq->status->num_total == 1 )  /* 08 Aug 2001 */
9087            drive_MCW_imseq( seq , isqDR_onoffwid , (XtPointer) isqDR_offwid ) ;
9088 #endif
9089          ALLOW_CLIPPING( seq , (0.0f < seq->top_clip) ) ;
9090          seq->valid = 2 ;
9091          RETURN( True );
9092       }
9093       break ;
9094 
9095       /*------- change helptext! -------*/
9096 
9097       case isqDR_imhelptext:{
9098         char *newtxt = (char *) drive_data ;
9099         int ii ;
9100 
9101         if( newtxt == NULL ) RETURN( False );
9102         ii = strlen(newtxt) ;
9103         if( ii == 0 ) RETURN( False );
9104 
9105         strncpy( seq->im_helptext , newtxt , ISQ_NHELP ) ;
9106         seq->im_helptext[ISQ_NHELP] = '\0' ;
9107         RETURN( True );
9108       }
9109       break ;
9110 
9111       /*------- display anew! -------*/
9112 
9113       case isqDR_reimage:
9114       case isqDR_reshow:
9115       case isqDR_overlay:
9116       case isqDR_display:{
9117          int n = PTOI(drive_data) ;
9118 
9119          if( ! seq->ignore_redraws )
9120             ISQ_redisplay( seq , n , drive_code ) ;
9121          RETURN( True );
9122       }
9123       break ;
9124 
9125       /*------- display bar anew [23 Aug 1998] -------*/
9126 
9127       case isqDR_rebar:{
9128          KILL_2XIM( seq->given_xbar , seq->sized_xbar ) ; /* destroy old */
9129          if( seq->onoff_state ) ISQ_show_bar( seq ) ;     /* show new?  */
9130          RETURN( True );
9131       }
9132       break ;
9133 
9134       /*------- new cursor for image -------*/
9135 
9136       case isqDR_cursor:{
9137          int cur = PTOI(drive_data) ;
9138 
9139          MCW_alter_widget_cursor( seq->wimage , cur , "yellow" , "blue" ) ;
9140          RETURN( True );
9141       }
9142       break ;
9143 
9144       /*------- new options -------*/
9145 
9146       case isqDR_options:{
9147          ISQ_options *newopt = (ISQ_options *) drive_data ;
9148          int sf=0 ;
9149 
9150          if( ppmto_num > 0 )           /* 27 Mar 2002: keep the old */
9151            sf = seq->opt.save_filter ; /*              save filter  */
9152 
9153          if( newopt != NULL ) seq->opt = *newopt ;
9154 
9155          if( ppmto_num > 0 )
9156            seq->opt.save_filter = sf ;
9157 
9158          seq->opt.parent = (XtPointer) seq ;
9159          SET_SAVE_LABEL(seq) ;
9160          AV_SENSITIZE(seq->ov_opacity_av,!seq->opt.no_overlay) ; /* 09 Mar 2001 */
9161 
9162 
9163          if( ISQ_REALZ(seq) ) ISQ_redisplay( seq , -1 , isqDR_display ) ;
9164          RETURN( True );
9165       }
9166       break ;
9167 
9168       /*------- get current options [07 Aug 1999] -------*/
9169 
9170       case isqDR_getoptions:{
9171          ISQ_options *opt = (ISQ_options *) drive_data ;
9172 
9173          if( opt == NULL ) RETURN( False );
9174          *opt = seq->opt ;
9175          RETURN( True );
9176       }
9177 
9178       /*------- turn arrowpad on -------*/
9179 
9180       case isqDR_arrowpadon:{
9181          char *helptext = (char *) drive_data ;
9182 
9183          XtSetMappedWhenManaged( seq->arrowpad->wform , True ); /* on */
9184 
9185          if( helptext != NULL && strlen(helptext) > 0 ){
9186             char *str = XtNewString( helptext ) ;
9187             MCW_reghelp_children( seq->arrowpad->wform , str ) ;
9188             XtFree(str) ;  /* 28 Sep 1998: via Purify */
9189          }
9190          RETURN( True );
9191       }
9192       break ;
9193 
9194       case isqDR_arrowpadhint:{
9195          int ib ;
9196          char ** hint = (char **) drive_data ;
9197          if( hint == NULL ) RETURN( False );
9198          for( ib=0 ; ib < 5 ; ib++ )
9199             MCW_register_hint( seq->arrowpad->wbut[ib] , hint[ib] ) ;
9200          RETURN( True );
9201       }
9202       break ;
9203 
9204       /*------- turn arrowpad off -------*/
9205 
9206       case isqDR_arrowpadoff:{
9207          XtSetMappedWhenManaged( seq->arrowpad->wform , False ); /* off */
9208          RETURN( True );
9209       }
9210       break ;
9211 
9212       /*------- new numtotal -------*/
9213 
9214 #if 0                                   /* 29 Jul 2002: removed from the canon for unuse */
9215       case isqDR_numtotal:{
9216          int newtot = (int) drive_data ,
9217              oldtot = seq->status->num_total ,
9218              numser = seq->status->num_series , ii ;
9219          char *msg =
9220              "illegal change to image\n"
9221              "count from driver routine\n"
9222              "(press here to continue)" ;
9223 
9224          /* check for error conditions */
9225 
9226          if( newtot == oldtot ) RETURN( True );
9227 
9228          if( newtot < 2 || newtot < numser ){
9229             if( ISQ_REALZ(seq) )
9230                MCW_popup_message( seq->wimage , msg , MCW_USER_KILL ) ;
9231             fprintf(stderr,"\n%s\n",msg) ;
9232             RETURN( False );
9233          }
9234 
9235          /* stop the automatic statistics calculations, if started */
9236 
9237 #ifdef AUTOMATE_STATISTICS  /* no longer allowed */
9238          if( seq->glstat->worker != 0 ){
9239             XtRemoveWorkProc( seq->glstat->worker ) ;
9240             seq->glstat->worker = 0 ;
9241          }
9242 #endif
9243 
9244          /* setup new space for the per image statistics */
9245 
9246          seq->imstat = (ISQ_indiv_statistics *)
9247                         XtRealloc( (char *) seq->imstat ,
9248                                    sizeof(ISQ_indiv_statistics) * newtot ) ;
9249 
9250          for( ii=oldtot ; ii < newtot ; ii++ )
9251              seq->imstat[ii].one_done = seq->imstat[ii].glob_done = False ;
9252 
9253          /* let the imseq know that the number of images is different */
9254 
9255          seq->status->num_total = newtot ;
9256 
9257          XtVaSetValues( seq->wscale ,
9258                            XmNmaximum , newtot-1 ,
9259                         NULL ) ;
9260 
9261          if( seq->im_nr >= newtot )
9262             ISQ_redisplay( seq , newtot-1 , isqDR_display ) ;
9263 
9264          RETURN( True );
9265       }
9266       break ;
9267 #endif
9268 
9269       /*------- new image sequence!!! -------*/
9270 
9271       case isqDR_newseq:{
9272          RwcBoolean good ;
9273          ISQ_timer_stop(seq) ;
9274          good = ISQ_setup_new( seq , drive_data ) ;
9275          RETURN( good );
9276       }
9277       break ;
9278 
9279       /*------ re-initialize image statistics -----*/
9280 
9281       case isqDR_clearstat:{
9282          int ii ;
9283 
9284          seq->opt.scale_group = ISQ_SCL_AUTO ;  /* autoscaling */
9285          ISQ_disp_options( seq , False ) ;      /* set buttons */
9286 
9287 #ifdef AUTOMATE_STATISTICS  /* no longer allowed */
9288          if( seq->glstat->worker != 0 ){  /* remove work process */
9289             XtRemoveWorkProc( seq->glstat->worker ) ;
9290             seq->glstat->worker = 0 ;
9291          }
9292 #endif
9293 
9294          for( ii=0 ; ii < seq->status->num_total ; ii++ )
9295             seq->imstat[ii].one_done = seq->imstat[ii].glob_done = False ;
9296 
9297          for( ii=0 ; ii < NHISTOG ; ii++ )
9298             seq->glstat->hist[ii] = 0 ;  /* initialize histogram */
9299 
9300          seq->glstat->mm_done =
9301            seq->glstat->per_done = (seq->status->num_series < 2 ) ;
9302 
9303 #ifdef AUTOMATE_STATISTICS
9304          if( seq->glstat->mm_done ){
9305             seq->glstat->worker = 0 ;
9306          } else {
9307             seq->glstat->worker = XtAppAddWorkProc(
9308                                         seq->dc->appcontext ,
9309                                         ISQ_statistics_WP , seq ) ;
9310          }
9311 #else
9312          seq->glstat->worker = 0 ;
9313 #endif
9314       }
9315       break ;
9316 
9317    }  /* end of switch on drive_code */
9318 
9319    RETURN( False );  /* should never be reached! */
9320 }
9321 
9322 /*---------------------------------------------------------------------*/
9323 
9324 #define XYORG 128
9325 #define DXY    64
9326 
ISQ_arrowpad_CB(MCW_arrowpad * apad,XtPointer client_data)9327 void ISQ_arrowpad_CB( MCW_arrowpad *apad , XtPointer client_data )
9328 {
9329    MCW_imseq *seq = (MCW_imseq *) client_data ;
9330 
9331    ISQ_cbs cbs ;
9332    int xorg,yorg , xwin,ywin , xoff,yoff ;
9333 
9334 ENTRY("ISQ_arrowpad_CB") ;
9335 
9336    if( ! ISQ_REALZ(seq) || seq->status->send_CB == NULL ) EXRETURN ;
9337 
9338    cbs.event = &(apad->xev) ;  /* copy event for user's edification */
9339 
9340    if( apad->which_pressed == AP_MID ){
9341       cbs.reason = isqCR_appress ;
9342 #if 0
9343       seq->status->send_CB( seq , seq->getaux , &cbs ) ;
9344 #else
9345       SEND(seq,cbs) ;
9346 #endif
9347       EXRETURN ;
9348    }
9349 
9350    /* 24 Jan 2003: pan a zoomed image */
9351 
9352    if( seq->zoom_button1 && seq->zoom_fac > 1 && seq->zoom_xim != NULL ){
9353      switch( apad->which_pressed ){
9354        default:
9355        case AP_DOWN:  xoff =  0 ; yoff = -1 ; break ;
9356        case AP_UP:    xoff =  0 ; yoff =  1 ; break ;
9357        case AP_LEFT:  xoff =  1 ; yoff =  0 ; break ;
9358        case AP_RIGHT: xoff = -1 ; yoff =  0 ; break ;
9359      }
9360      ISQ_actually_pan( seq , xoff , yoff ) ;
9361      EXRETURN ;
9362    }
9363 
9364    xwin = ywin = XYORG ;
9365 
9366    switch( apad->which_pressed ){
9367       default:
9368       case AP_DOWN:  ywin = XYORG + DXY ; break ;
9369       case AP_UP:    ywin = XYORG - DXY ; break ;
9370       case AP_LEFT:  xwin = XYORG - DXY ; break ;
9371       case AP_RIGHT: xwin = XYORG + DXY ; break ;
9372    }
9373 
9374    xorg = yorg = XYORG ;       ISQ_flipxy( seq , &xorg,&yorg ) ;
9375    xoff = xwin ; yoff = ywin ; ISQ_flipxy( seq , &xoff,&yoff ) ;
9376 
9377         if( xoff > xorg ) cbs.reason = isqCR_dxplus  ;
9378    else if( xoff < xorg ) cbs.reason = isqCR_dxminus ;
9379    else if( yoff > yorg ) cbs.reason = isqCR_dyplus  ;
9380    else if( yoff < yorg ) cbs.reason = isqCR_dyminus ;
9381    else                   EXRETURN ;                     /* error! */
9382 
9383 #if 0
9384    seq->status->send_CB( seq , seq->getaux , &cbs ) ;
9385 #else
9386    SEND(seq,cbs) ;
9387 #endif
9388    EXRETURN ;
9389 }
9390 
9391 /*-------------------------------------------------------------------
9392    Setup the data structures to handle a new sequence of images;
9393    this should be immediately followed by a call to set the image
9394    to the correct number (or things won't look good at all).
9395 ---------------------------------------------------------------------*/
9396 
ISQ_setup_new(MCW_imseq * seq,XtPointer newaux)9397 RwcBoolean ISQ_setup_new( MCW_imseq *seq , XtPointer newaux )
9398 {
9399    MCW_imseq_status *imstatus=NULL ;
9400    int ii ;
9401    MRI_IMAGE *tim ;
9402 
9403 ENTRY("ISQ_setup_new") ;
9404 
9405    if( !ISQ_VALID(seq) ) RETURN( False );
9406 
9407 #if 0
9408    imstatus = (MCW_imseq_status *)seq->getim(0,isqCR_getstatus,newaux);
9409 #else
9410    AFNI_CALL_VALU_3ARG( seq->getim , MCW_imseq_status *,imstatus ,
9411                         int,0 , int,isqCR_getstatus , XtPointer,newaux ) ;
9412 #endif
9413    if( imstatus->num_total < 1 ){ RETURN( False ); }  /* 09 Feb 1999: allow 1 */
9414 
9415 #if 0
9416    tim = (MRI_IMAGE *) seq->getim(0,isqCR_getqimage,newaux) ; /* 1st image */
9417    KILL_1MRI(tim) ;  /* don't need tim no more */
9418 #endif
9419 
9420 #if 1
9421    if( seq->status != NULL ) myXtFree(seq->status) ;  /* 05 Feb 2000 */
9422 #endif
9423 
9424    seq->status = imstatus ;
9425    seq->im_nr  = imstatus->num_total / 2 ;  /* do this image 1st */
9426 
9427    KILL_1MRI(seq->imim) ;  /* NULL out all internally stored images */
9428    KILL_1MRI(seq->ovim) ;
9429    KILL_1MRI(seq->orim) ;  /* 09 Feb 1999 */
9430 
9431    KILL_2XIM( seq->given_xim  , seq->sized_xim  ) ;
9432    KILL_2XIM( seq->given_xbar , seq->sized_xbar ) ;
9433 
9434    seq->given_xim = seq->sized_xim
9435                   = seq->given_xbar
9436                   = seq->sized_xbar = NULL ;
9437 
9438    seq->imim = seq->ovim = NULL ;
9439 
9440    /* re-initialize image statistics */
9441 
9442    seq->opt.scale_group = ISQ_SCL_AUTO ;
9443    ISQ_disp_options( seq , False ) ;  /* set toggles from option list */
9444 
9445    seq->imstat = (ISQ_indiv_statistics *)
9446                  XtRealloc( (char *) seq->imstat ,
9447                             sizeof(ISQ_indiv_statistics)
9448                             * imstatus->num_total ) ;
9449 
9450 #ifdef AUTOMATE_STATISTICS  /* no longer allowed */
9451    if( seq->glstat->worker != 0 ){  /* remove work process, if started */
9452       XtRemoveWorkProc( seq->glstat->worker ) ;
9453       seq->glstat->worker = 0 ;
9454    }
9455 #endif
9456 
9457    for( ii=0 ; ii < imstatus->num_total ; ii++ ){
9458       seq->imstat[ii].one_done = seq->imstat[ii].glob_done = False ;
9459       seq->imstat[ii].parent   = (XtPointer) seq ;
9460    }
9461    seq->glstat->parent = (XtPointer) seq ;
9462 
9463    for( ii=0 ; ii < NHISTOG ; ii++ )
9464       seq->glstat->hist[ii] = 0 ;  /* initialize histogram */
9465 
9466    seq->glstat->mm_done =
9467      seq->glstat->per_done = (seq->status->num_series < 2 ) ;
9468 
9469 #ifdef AUTOMATE_STATISTICS
9470    if( seq->glstat->mm_done ){
9471       seq->glstat->worker = 0 ;
9472    } else {
9473       seq->glstat->worker = XtAppAddWorkProc(
9474                                   seq->dc->appcontext ,
9475                                   ISQ_statistics_WP , seq ) ;
9476    }
9477 #else
9478    seq->glstat->worker = 0 ;
9479 #endif
9480 
9481    /* OOPS!  I forgot to reset the scale max value! */
9482 
9483    ii = seq->status->num_total - 1 ; if( ii <= 0 ) ii = 1 ;  /* 09 Feb 1999 */
9484 
9485    XtVaSetValues( seq->wscale ,
9486                      XmNmaximum , ii ,
9487                      XmNvalue   , seq->im_nr ,
9488                   NULL ) ;
9489 
9490 #ifndef DONT_ONOFF_ONE
9491    if( seq->status->num_total == 1 )
9492       drive_MCW_imseq( seq , isqDR_onoffwid , (XtPointer) isqDR_offwid ) ;
9493 #endif
9494 
9495  if(PRINT_TRACING){
9496    char str[256] ;
9497    sprintf(str,"hbase=%d vbase=%d nim=%d lev=%g",
9498           seq->hbase,seq->vbase, seq->status->num_total,seq->lev ) ;
9499    STATUS(str) ;
9500  }
9501 
9502    seq->getaux = newaux ;
9503 
9504    seq->top_clip = 0.0f ;  /* 17 Sep 2007 */
9505    if( seq->opt.scale_range == ISQ_RNG_CLIPPED ) seq->redo_clip = 1 ;
9506    ALLOW_CLIPPING(seq,0) ;
9507 
9508    RETURN( True ) ;
9509 }
9510 
9511 /*----------------------------------------------------------------------------*/
9512 /*----            Stuff for the menu hidden on the color bar              ----*/
9513 
ISQ_wbar_plots_CB(Widget w,XtPointer cld,XtPointer cad)9514 void ISQ_wbar_plots_CB( Widget w , XtPointer cld , XtPointer cad ) /* 20 Sep 2001 */
9515 {
9516    MCW_imseq *seq = (MCW_imseq *) cld ;
9517 ENTRY("ISQ_wbar_plots_CB") ;
9518    if( ISQ_REALZ(seq) ) ISQ_redisplay( seq , -1 , isqDR_display ) ;
9519    EXRETURN ;
9520 }
9521 
9522 /*----------------------------------------------------------------------------*/
9523 
ISQ_wbar_amask_CB(Widget w,XtPointer client_data,XtPointer call_data)9524 void ISQ_wbar_amask_CB( Widget w, XtPointer client_data, XtPointer call_data )
9525 {
9526    MCW_imseq *seq = (MCW_imseq *)client_data ;  /* 14 Jun 2010 */
9527 ENTRY("ISQ_wbar_amask_CB") ;
9528    KILL_1MRI(seq->last_automask) ;
9529    if( ISQ_REALZ(seq) ) ISQ_redisplay( seq , -1 , isqDR_display ) ;
9530    EXRETURN ;
9531 }
9532 
9533 /*----------------------------------------------------------------------------*/
9534 
ISQ_wbar_invrt_CB(Widget w,XtPointer client_data,XtPointer call_data)9535 void ISQ_wbar_invrt_CB( Widget w, XtPointer client_data, XtPointer call_data )
9536 {
9537    MCW_imseq *seq = (MCW_imseq *)client_data ;  /* 14 Jun 2010 */
9538 ENTRY("ISQ_wbar_invrt_CB") ;
9539    if( ISQ_REALZ(seq) ) ISQ_redisplay( seq , -1 , isqDR_display ) ;
9540    EXRETURN ;
9541 }
9542 
9543 /*----------------------------------------------------------------------------*/
9544 
ISQ_wbar_crop_CB(Widget w,XtPointer client_data,XtPointer call_data)9545 void ISQ_wbar_crop_CB( Widget w, XtPointer client_data, XtPointer call_data )
9546 {
9547    MCW_imseq *seq = (MCW_imseq *)client_data ;  /* 14 Jun 2010 */
9548 ENTRY("ISQ_wbar_crop_CB") ;
9549    if( ISQ_REALZ(seq) ){
9550      seq->crop_autocenter = MCW_val_bbox(seq->wbar_crop_bbox) ;
9551      ISQ_redisplay( seq , -1 , isqDR_display ) ;
9552    }
9553    EXRETURN ;
9554 }
9555 
9556 /*----------------------------------------------------------------------*/
9557 
ISQ_wbar_label_CB(MCW_arrowval * av,XtPointer cd)9558 void ISQ_wbar_label_CB( MCW_arrowval *av , XtPointer cd )
9559 {
9560    MCW_imseq *seq = (MCW_imseq *)cd ;
9561 
9562 ENTRY("ISQ_wbar_label_CB") ;
9563 
9564    if( !ISQ_REALZ(seq) ) EXRETURN ;
9565 
9566    if( av == seq->wbar_animdup_av )
9567      ISQ_anim_dup = av->ival;  /* 10 Feb 2009 */
9568    else
9569      ISQ_redisplay( seq , -1 , isqDR_display ) ;
9570 
9571    EXRETURN ;
9572 }
9573 
9574 #if 1
ISQ_wbar_globrange_CB(MCW_arrowval * av,XtPointer cd)9575 void ISQ_wbar_globrange_CB( MCW_arrowval *av , XtPointer cd )
9576 {
9577    MCW_imseq *seq = (MCW_imseq *)cd ;
9578    ISQ_cbs cbs ;
9579 
9580 ENTRY("ISQ_wbar_globrange_CB") ;
9581 
9582    if( !ISQ_REALZ(seq) ) EXRETURN ;
9583 
9584    THD_set_image_globalrange(av->ival);
9585    cbs.reason = isqCR_resetglobalrange ;
9586 /*       cbs.key      = ii ;*/                 /* number of points */
9587 
9588        SEND(seq,cbs) ;   /* send this back to a callback function now in afni.c
9589                             imseq doesn't have access directly to dataset info */
9590 
9591 /*   THD_set_image_globalrange_env(av->ival);*/
9592 
9593    EXRETURN ;
9594 }
9595 #endif
9596 
9597 
9598 /*----------------------------------------------------------------------*/
9599 /* Finalize the overlay label append string [23 Dec 2011] */
9600 
ISQ_overlay_label_CB(Widget w,XtPointer fd,MCW_choose_cbs * cbs)9601 void ISQ_overlay_label_CB( Widget w , XtPointer fd , MCW_choose_cbs *cbs )
9602 {
9603    MCW_imseq *seq = (MCW_imseq *)fd ;
9604 ENTRY("ISQ_overlay_label_CB") ;
9605    if( seq->overlay_label != NULL ){
9606      free(seq->overlay_label) ; seq->overlay_label = NULL ;
9607    }
9608    if( cbs       != NULL && cbs->reason == mcwCR_string       &&
9609        cbs->cval != NULL && strcasecmp(cbs->cval,"NULL") != 0   ){
9610      seq->overlay_label = strdup(cbs->cval) ;
9611    }
9612    ISQ_redisplay( seq , -1 , isqDR_display ) ;
9613    EXRETURN ;
9614 }
9615 
9616 /*----------------------------------------------------------------------*/
9617 
ISQ_wbar_menu_CB(Widget w,XtPointer client_data,XtPointer call_data)9618 void ISQ_wbar_menu_CB( Widget w , XtPointer client_data ,
9619                                   XtPointer call_data    )
9620 {
9621    MCW_imseq *seq = (MCW_imseq *) client_data ;
9622 
9623 ENTRY("ISQ_wbar_menu_CB") ;
9624 
9625    if( ! ISQ_REALZ(seq) ) EXRETURN ;
9626 
9627    /*** User range toggle ***/
9628 
9629    if( w == seq->wbar_rng_but ){
9630       MCW_choose_string( seq->wimage , "Display range: bot top [ztop]" ,
9631                          NULL , ISQ_set_rng_CB , seq ) ;
9632    }
9633 
9634    else if( w == seq->wbar_zer_but ){
9635       MCW_choose_ovcolor( seq->wimage , seq->dc , seq->zer_color ,
9636                           ISQ_set_zcol_CB , seq ) ;
9637    }
9638 
9639    else if( w == seq->wbar_flat_but ){
9640       MCW_choose_string( seq->wimage , "Flatten range: bot top" ,
9641                          NULL , ISQ_set_flat_CB , seq ) ;
9642    }
9643 
9644    else if( w == seq->wbar_sharp_but ){
9645       MCW_choose_integer( seq->wimage , "Sharpen Factor" ,
9646                           1 , 9 , (int)(10.01*seq->sharp_fac) ,
9647                           ISQ_set_sharp_CB , seq ) ;
9648    }
9649 
9650    else if( w == seq->wbar_vgize_but ){
9651       MCW_choose_integer( seq->wimage , "VG Factor" ,
9652                           1 , 9 , VGFAC_TO_INDEX(seq->vgize_fac) ,
9653                           ISQ_set_vgize_CB , seq ) ;
9654    }
9655 
9656    else if( w == seq->wbar_graymap_pb ){   /* 24 Oct 2003 */
9657      ISQ_graymap_draw( seq ) ;
9658    }
9659 
9660    else if( w == seq->wbar_labst_pb ){     /* 23 Dec 2011 */
9661      MCW_choose_string( w , "Overlay Label Append String" ,
9662                             seq->overlay_label , ISQ_overlay_label_CB , seq ) ;
9663    }
9664 
9665    EXRETURN ;
9666 }
9667 
9668 /*----------------------------------------------------------------------*/
9669 
ISQ_set_rng_CB(Widget w,XtPointer cd,MCW_choose_cbs * cbs)9670 void ISQ_set_rng_CB( Widget w , XtPointer cd , MCW_choose_cbs *cbs )
9671 {
9672    MCW_imseq *seq = (MCW_imseq *) cd ;
9673 
9674 ENTRY("ISQ_set_rng_CB") ;
9675 
9676    if( ! ISQ_REALZ(seq) || w == NULL || ! XtIsWidget(w) ) EXRETURN ;
9677 
9678    seq->rng_bot = seq->rng_top = seq->rng_ztop = 0.0 ;
9679    seq->rng_extern = 0 ;
9680    sscanf( cbs->cval , "%f%f%f" ,
9681            &(seq->rng_bot) , &(seq->rng_top) , &(seq->rng_ztop) ) ;
9682    ISQ_redisplay( seq , -1 , isqDR_reimage ) ;  /* redo current image */
9683    EXRETURN ;
9684 }
9685 
9686 /*----------------------------------------------------------------------*/
9687 
ISQ_set_zcol_CB(Widget w,XtPointer cd,MCW_choose_cbs * cbs)9688 void ISQ_set_zcol_CB( Widget w , XtPointer cd , MCW_choose_cbs *cbs )
9689 {
9690    MCW_imseq *seq = (MCW_imseq *) cd ;
9691 
9692 ENTRY("ISQ_set_zcol_CB") ;
9693 
9694    if( ! ISQ_REALZ(seq) || w == NULL || ! XtIsWidget(w) ) EXRETURN ;
9695 
9696    seq->zer_color = cbs->ival ;
9697    ISQ_redisplay( seq , -1 , isqDR_reimage ) ;  /* redo current image */
9698    EXRETURN ;
9699 }
9700 
9701 /*----------------------------------------------------------------------*/
9702 
ISQ_set_flat_CB(Widget w,XtPointer cd,MCW_choose_cbs * cbs)9703 void ISQ_set_flat_CB( Widget w , XtPointer cd , MCW_choose_cbs *cbs )
9704 {
9705    MCW_imseq *seq = (MCW_imseq *) cd ;
9706 
9707 ENTRY("ISQ_set_flat_CB") ;
9708 
9709    if( ! ISQ_REALZ(seq) || w == NULL || ! XtIsWidget(w) ) EXRETURN ;
9710 
9711    seq->flat_bot = seq->flat_top = 0.0 ;
9712    sscanf( cbs->cval , "%f%f" ,
9713            &(seq->flat_bot) , &(seq->flat_top) ) ;
9714 
9715    if( seq->flat_bot < 0.0 ) seq->flat_bot = 0.0 ;
9716    if( seq->flat_bot > 1.0 ) seq->flat_bot*= 0.01 ;
9717    if( seq->flat_top < 0.0 ) seq->flat_top = 0.0 ;
9718    if( seq->flat_top > 1.0 ) seq->flat_top*= 0.01 ;
9719 
9720    if( seq->flat_bot >= seq->flat_top || seq->flat_top > 1.0 )
9721       seq->flat_bot = seq->flat_top = 0.0 ;
9722 
9723    ISQ_redisplay( seq , -1 , isqDR_reimage ) ;  /* redo current image */
9724    EXRETURN ;
9725 }
9726 
9727 /*----------------------------------------------------------------------*/
9728 
ISQ_set_sharp_CB(Widget w,XtPointer cd,MCW_choose_cbs * cbs)9729 void ISQ_set_sharp_CB( Widget w , XtPointer cd , MCW_choose_cbs *cbs )
9730 {
9731    MCW_imseq *seq = (MCW_imseq *) cd ;
9732 
9733 ENTRY("ISQ_set_sharp_CB") ;
9734 
9735    if( ! ISQ_REALZ(seq) || w == NULL || ! XtIsWidget(w) ) EXRETURN ;
9736 
9737    seq->sharp_fac = 0.1 * cbs->ival ;
9738 
9739    ISQ_redisplay( seq , -1 , isqDR_reimage ) ;  /* redo current image */
9740    EXRETURN ;
9741 }
9742 
9743 /*----------------------------------------------------------------------*/
9744 
ISQ_set_vgize_CB(Widget w,XtPointer cd,MCW_choose_cbs * cbs)9745 void ISQ_set_vgize_CB( Widget w , XtPointer cd , MCW_choose_cbs *cbs )
9746 {
9747    MCW_imseq *seq = (MCW_imseq *) cd ;
9748 
9749 ENTRY("ISQ_set_vgize_CB") ;
9750 
9751    if( ! ISQ_REALZ(seq) || w == NULL || ! XtIsWidget(w) ) EXRETURN ;
9752 
9753    seq->vgize_fac = INDEX_TO_VGFAC(cbs->ival) ;
9754 
9755    ISQ_redisplay( seq , -1 , isqDR_reimage ) ;  /* redo current image */
9756    EXRETURN ;
9757 }
9758 
9759 /*----------------------------------------------------------------------------
9760    April 1996: Routines to process the montage stuff
9761 ------------------------------------------------------------------------------*/
9762 
9763 #define MONT_quit_label  "Quit"
9764 #define MONT_1x1_label   "1x1"
9765 #define MONT_apply_label "Draw"
9766 #define MONT_done_label  "Set"
9767 
9768 #define MONT_quit_help   "Press to close\nthis control box"
9769 #define MONT_1x1_help    "Press to set the controls\nto Across=1 and Down=1"
9770 #define MONT_apply_help  "Press to apply this choice\nand keep this control box"
9771 #define MONT_done_help   "Press to apply this choice\nand close this control box"
9772 
9773 #define NUM_MONT_ACT 4
9774 
9775 static MCW_action_item MONT_act[NUM_MONT_ACT] = {
9776  { MONT_quit_label , ISQ_montage_action_CB, NULL, MONT_quit_help ,"Close window"                 ,0 },
9777  { MONT_1x1_label  , ISQ_montage_action_CB, NULL, MONT_1x1_help  ,"Set Across=Down=1"            ,0 },
9778  { MONT_apply_label, ISQ_montage_action_CB, NULL, MONT_apply_help,"Apply choice and keep window" ,0 },
9779  { MONT_done_label , ISQ_montage_action_CB, NULL, MONT_done_help ,"Apply choice and close window",1 },
9780 } ;
9781 
9782 #define MONT_QUIT  0
9783 #define MONT_1X1   1
9784 #define MONT_APPLY 2
9785 #define MONT_DONE  3
9786 
ISQ_montage_CB(Widget w,XtPointer client_data,XtPointer call_data)9787 void ISQ_montage_CB( Widget w, XtPointer client_data, XtPointer call_data )
9788 {
9789    MCW_imseq *seq = (MCW_imseq *) client_data ;
9790    int ib ;
9791    Widget wrc ;
9792 
9793 ENTRY("ISQ_montage_CB") ;
9794 
9795    if( ! ISQ_REALZ(seq) || seq->dialog != NULL ) EXRETURN ;
9796 
9797    if( seq->zoom_fac != 1 ){
9798 #if 0
9799 fprintf(stderr,"montage: zoom_fac = %d\n",seq->zoom_fac) ;
9800 #endif
9801      /* XBell(seq->dc->display,100); */ EXRETURN; /* 18 Nov 2003 */
9802    }
9803 
9804    for( ib=0 ; ib < NBUTTON_BOT-1 ; ib++ )       /* turn off buttons  */
9805      if( ISQ_but_bot_dial[ib] == True )          /* that also want to */
9806        SENSITIZE( seq->wbut_bot[ib] , False ) ;  /* use seq->dialog   */
9807 
9808    seq->dialog = XtVaCreatePopupShell(
9809                     "menu" , xmDialogShellWidgetClass , seq->wtop ,
9810                        XmNtitle , "Montage" ,
9811                        XmNdeleteResponse , XmDO_NOTHING ,
9812                        XmNinitialResourcesPersistent , False ,
9813                     NULL ) ;
9814 
9815    SAVEUNDERIZE(seq->dialog) ; /* 27 Feb 2001 */
9816 
9817    DC_yokify( seq->dialog , seq->dc ) ; /* 14 Sep 1998 */
9818 
9819    seq->dialog_starter = NBUT_MONT ;
9820 
9821 #if 1
9822    if( MCW_isitmwm(w) )
9823       XtVaSetValues( seq->dialog ,
9824                        XmNmwmDecorations , MWM_DECOR_BORDER ,
9825                        XmNmwmFunctions ,   MWM_FUNC_MOVE
9826                                          | MWM_FUNC_CLOSE ,
9827                      NULL ) ;
9828 #endif
9829 
9830    XmAddWMProtocolCallback(           /* make "Close" window menu work */
9831            seq->dialog ,
9832            XmInternAtom( seq->dc->display , "WM_DELETE_WINDOW" , False ) ,
9833            ISQ_montage_action_CB , seq ) ;
9834 
9835    wrc  = XtVaCreateWidget(                    /* RowColumn to hold all */
9836              "menu" , xmRowColumnWidgetClass , seq->dialog ,
9837                 XmNpacking     , XmPACK_TIGHT ,
9838                 XmNorientation , XmVERTICAL ,
9839                 XmNtraversalOn , False ,
9840                 XmNinitialResourcesPersistent , False ,
9841              NULL ) ;
9842 
9843    wwtem = XtVaCreateManagedWidget(
9844             "menu" , xmLabelWidgetClass , wrc ,
9845                LABEL_ARG("-- Montage Controls --") ,
9846                XmNalignment  , XmALIGNMENT_CENTER ,
9847                XmNinitialResourcesPersistent , False ,
9848             NULL ) ; LABELIZE(wwtem) ;
9849 
9850    (void) XtVaCreateManagedWidget(
9851             "menu" , xmSeparatorWidgetClass , wrc ,
9852                XmNseparatorType , XmSHADOW_ETCHED_IN ,
9853                XmNinitialResourcesPersistent , False ,
9854             NULL ) ;
9855 
9856    seq->mont_across_av = new_MCW_arrowval(
9857                           wrc , "Across:" ,
9858                           MCW_AV_optmenu ,
9859                           1 , MONT_NMAX , seq->mont_nx ,
9860                           MCW_AV_edittext , 0 ,
9861                           NULL , NULL , NULL , NULL ) ;
9862 
9863    if( MONT_NMAX > COLSIZE )
9864       AVOPT_columnize(  seq->mont_across_av , 1+(MONT_NMAX-1)/COLSIZE ) ;
9865 
9866    if( seq->mont_across_av->wtext != NULL )
9867       XtVaSetValues( seq->mont_across_av->wtext , XmNcolumns , 4 , NULL ) ;
9868 
9869    seq->mont_down_av  = new_MCW_arrowval(
9870                           wrc , "Down:  " ,
9871                           MCW_AV_optmenu ,
9872                           1 , MONT_NMAX , seq->mont_ny ,
9873                           MCW_AV_edittext , 0 ,
9874                           NULL , NULL , NULL , NULL ) ;
9875 
9876    if( MONT_NMAX > COLSIZE )
9877       AVOPT_columnize(  seq->mont_down_av , 1+(MONT_NMAX-1)/COLSIZE ) ;
9878 
9879    if( seq->mont_down_av->wtext != NULL )
9880       XtVaSetValues( seq->mont_down_av->wtext , XmNcolumns , 4 , NULL ) ;
9881 
9882    seq->mont_skip_av  = new_MCW_arrowval(
9883                           wrc , "Spacing" ,
9884                           MCW_AV_optmenu ,
9885                           1 , MONT_SMAX , seq->mont_skip + 1 ,
9886                           MCW_AV_edittext , 0 ,
9887                           NULL , NULL , NULL , NULL ) ;
9888 
9889    if( MONT_SMAX > COLSIZE )
9890       AVOPT_columnize(  seq->mont_skip_av , 1+(MONT_SMAX-1)/COLSIZE ) ;
9891 
9892    if( seq->mont_skip_av->wtext != NULL )
9893       XtVaSetValues( seq->mont_skip_av->wtext , XmNcolumns , 4 , NULL ) ;
9894 
9895    seq->mont_gap_av  = new_MCW_arrowval(
9896                           wrc , "Border:" ,
9897                           MCW_AV_optmenu ,
9898                           0 , MONT_GMAX , seq->mont_gap,
9899                           MCW_AV_edittext , 0 ,
9900                           NULL , NULL , NULL , NULL ) ;
9901 
9902    if( MONT_GMAX > COLSIZE )
9903       AVOPT_columnize(  seq->mont_gap_av , 1+(MONT_GMAX-1)/COLSIZE ) ;
9904 
9905    if( seq->mont_gap_av->wtext != NULL )
9906       XtVaSetValues( seq->mont_gap_av->wtext , XmNcolumns , 4 , NULL ) ;
9907 
9908    seq->mont_gapcolor_av = new_MCW_colormenu( wrc ,
9909                                 "Color: " , seq->dc ,
9910                                 0 , seq->dc->ovc->ncol_ov - 1 , seq->mont_gapcolor ,
9911                                 NULL , NULL ) ;
9912 
9913 if( AFNI_yesenv("TMONT") ){
9914    seq->mont_type_av = new_MCW_arrowval(
9915                           wrc , "Type:  " ,
9916                           MCW_AV_optmenu ,
9917                           0 , MONT_LAST_TYPE , seq->mont_mode,
9918                           MCW_AV_edittext , 0 ,
9919                           NULL , NULL , MCW_av_substring_CB , mont_types ) ;
9920    MCW_reghint_children( seq->mont_across_av->wrowcol ,
9921                          "Spatial or Temporal montage?" ) ;
9922 } else {
9923    seq->mont_type_av = NULL ;
9924 }
9925 
9926 #if 0
9927    seq->mont_across_av->allow_wrap   = 1 ;   /* allow wrap at limits of values */
9928    seq->mont_down_av->allow_wrap     = 1 ;
9929    seq->mont_skip_av->allow_wrap     = 1 ;
9930    seq->mont_gap_av->allow_wrap      = 1 ;
9931    seq->mont_gapcolor_av->allow_wrap = 1 ;
9932 
9933    seq->mont_across_av->fastdelay    = 250 ; /* slow down arrow repeat action */
9934    seq->mont_down_av->fastdelay      = 250 ;
9935    seq->mont_skip_av->fastdelay      = 250 ;
9936    seq->mont_gap_av->fastdelay       = 250 ;
9937    seq->mont_gapcolor_av->fastdelay  = 250 ;
9938 #endif
9939 
9940    seq->mont_nx_old       = seq->mont_nx       ; /* in case something is changed */
9941    seq->mont_ny_old       = seq->mont_ny       ;
9942    seq->mont_skip_old     = seq->mont_skip     ;
9943    seq->mont_gap_old      = seq->mont_gap      ;
9944    seq->mont_gapcolor_old = seq->mont_gapcolor ;
9945    seq->mont_mode_old     = seq->mont_mode     ;
9946 
9947    MCW_reghelp_children( seq->mont_across_av->wrowcol ,
9948       "This controls the number\n"
9949       "of images displayed across\n"
9950       "(horizontally) the window."
9951    ) ;
9952    MCW_reghint_children( seq->mont_across_av->wrowcol ,
9953                          "Number of images horizontal" ) ;
9954 
9955    MCW_reghelp_children( seq->mont_down_av->wrowcol ,
9956       "This controls the number\n"
9957       "of images displayed down\n"
9958       "(vertically) the window."
9959    ) ;
9960    MCW_reghint_children( seq->mont_down_av->wrowcol ,
9961                          "Number of images vertical" ) ;
9962 
9963    MCW_reghelp_children( seq->mont_skip_av->wrowcol ,
9964       "This controls the spacing between\n"
9965       "slice images displayed in the\n"
9966       "montage.  For example, if Spacing\n"
9967       "is 4, every fourth slice will be\n"
9968       "displayed (from left to right, then\n"
9969       "top to bottom)."
9970    ) ;
9971    MCW_reghint_children( seq->mont_skip_av->wrowcol ,
9972                          "Spacing between images" ) ;
9973 
9974    MCW_reghelp_children( seq->mont_gap_av->wrowcol ,
9975       "This controls the number\n"
9976       "of pixels left as borders\n"
9977       "between the sub-images"
9978    ) ;
9979    MCW_reghint_children( seq->mont_gap_av->wrowcol ,
9980                          "Borders between images" ) ;
9981 
9982    MCW_reghelp_children( seq->mont_gapcolor_av->wrowcol ,
9983       "This controls the color\n"
9984       "put in the borders between\n"
9985       "the sub-images"
9986    ) ;
9987    MCW_reghint_children( seq->mont_gapcolor_av->wrowcol ,
9988                          "Border color" ) ;
9989 
9990    for( ib=0 ; ib < NUM_MONT_ACT ; ib++ )
9991       MONT_act[ib].data = (XtPointer) seq ;
9992 
9993    (void) MCW_action_area( wrc , MONT_act , NUM_MONT_ACT ) ;
9994 
9995    XtManageChild( wrc ) ;
9996    ISQ_place_dialog( seq ) ;  /* 05 Jan 1999 */
9997    XtPopup( seq->dialog , XtGrabNone ) ; NI_sleep(1);
9998    NORMAL_cursorize( seq->dialog ) ;
9999    ISQ_but_done_reset( seq ) ;
10000    EXRETURN ;
10001 }
10002 
10003 /*----------------------------------------------------------------------------*/
10004 
ISQ_montage_action_CB(Widget w,XtPointer client_data,XtPointer call_data)10005 void ISQ_montage_action_CB( Widget w , XtPointer client_data , XtPointer call_data )
10006 {
10007    MCW_imseq *seq = (MCW_imseq *) client_data ;
10008    XmAnyCallbackStruct *cbs = (XmAnyCallbackStruct *) call_data ;
10009    char *wname ;
10010    int ib , close_window , new_mont ;
10011 
10012 ENTRY("ISQ_montage_action_CB") ;
10013 
10014    if( !ISQ_REALZ(seq) || seq->dialog==NULL || seq->dialog_starter!=NBUT_MONT ) EXRETURN ;
10015 
10016    wname = XtName(w) ;
10017 
10018    for( ib=0 ; ib < NUM_MONT_ACT ; ib++ )           /* button index, if any */
10019       if( strcmp(wname,MONT_act[ib].label) == 0 ) break ;
10020 
10021    close_window = (ib == MONT_DONE || ib == MONT_QUIT || ib == NUM_MONT_ACT) ;
10022 
10023    if( close_window ){
10024      RWC_XtPopdown( seq->dialog ) ;
10025      XSync( XtDisplay(w) , False ) ;
10026      XmUpdateDisplay( w ) ;
10027      seq->dont_place_dialog = 1 ;  /* 23 Jan 2004 */
10028    }
10029 
10030    switch( ib ){
10031 
10032       case MONT_APPLY:
10033       case MONT_DONE:
10034          seq->mont_nx       = seq->mont_across_av->ival ;
10035          seq->mont_ny       = seq->mont_down_av->ival ;
10036          seq->mont_skip     = seq->mont_skip_av->ival - 1 ;
10037          seq->mont_gap      = seq->mont_gap_av->ival ;
10038          seq->mont_gapcolor = seq->mont_gapcolor_av->ival ;
10039 
10040          new_mont = ( seq->mont_nx   != seq->mont_nx_old ||
10041                       seq->mont_ny   != seq->mont_ny_old ||
10042                       seq->mont_skip != seq->mont_skip_old ) ;
10043 
10044          if( seq->mont_type_av != NULL ){
10045            seq->mont_mode = seq->mont_type_av->ival ;
10046            new_mont = new_mont || (seq->mont_mode != seq->mont_mode_old) ;
10047          }
10048 
10049          if( ib == MONT_APPLY ) MCW_invert_widget(w) ;
10050 
10051          ISQ_redisplay( seq , -1 , isqDR_display ) ;    /* local redraw */
10052 
10053          if( seq->status->send_CB != NULL && new_mont ){
10054 
10055             ISQ_cbs cbs ;
10056             THD_ivec3 minf ;
10057             int ijcen = (seq->mont_nx)/2 + (seq->mont_ny/2) * seq->mont_nx ,
10058                 nmont = seq->mont_nx * seq->mont_ny ;
10059 
10060             minf.ijk[0]  = ijcen ;            /* number of slices before center */
10061             minf.ijk[1]  = nmont-ijcen-1 ;    /* number after */
10062             minf.ijk[2]  = seq->mont_skip ;   /* number between slices */
10063             cbs.reason   = isqCR_newmontage ;
10064             cbs.userdata = (XtPointer) &minf ;
10065 
10066             seq->ignore_redraws = 1 ;         /* don't listen to redraws */
10067 #if 0
10068             seq->status->send_CB( seq , seq->getaux , &cbs ) ;
10069 #else
10070             SEND(seq,cbs) ;
10071 #endif
10072             seq->ignore_redraws = 0 ;         /* can listen again */
10073          }
10074 
10075 #if 0
10076          ISQ_redisplay( seq , -1 , isqDR_display ) ;    /* local redraw */
10077 #endif
10078 
10079          if( ib == MONT_APPLY ) MCW_invert_widget(w) ;
10080 
10081          seq->mont_nx_old       = seq->mont_nx ;
10082          seq->mont_ny_old       = seq->mont_ny ;
10083          seq->mont_skip_old     = seq->mont_skip ;
10084          seq->mont_gap_old      = seq->mont_gap ;
10085          seq->mont_gapcolor_old = seq->mont_gapcolor ;
10086 
10087          /* set to "Save One" if have an actual montage going now */
10088 
10089          if( seq->mont_nx * seq->mont_ny > 1 && !seq->opt.save_one ){
10090             seq->opt.save_one  = 1 ;
10091             seq->opt.save_agif = 0 ; /* 27 Jul 2001 */
10092             seq->opt.save_mpeg = 0 ;
10093             SET_SAVE_LABEL(seq) ;
10094          }
10095       break ;
10096 
10097       case MONT_1X1:
10098          MCW_invert_widget(w) ;
10099          AV_assign_ival( seq->mont_across_av , 1 ) ;
10100          AV_assign_ival( seq->mont_down_av   , 1 ) ;
10101          MCW_invert_widget(w) ;
10102       break ;
10103    }
10104 
10105    /*** done -- close the window if ordered ***/
10106 
10107    if( close_window ){                          /* close the window */
10108       XtDestroyWidget( seq->dialog ) ; NI_sleep(1) ;
10109       seq->dialog = NULL ;
10110       for( ib=0 ; ib < NBUTTON_BOT-1 ; ib++ )       /* turn buttons back on */
10111         if( ISQ_but_bot_dial[ib] == True )         /* that also want to    */
10112           SENSITIZE( seq->wbut_bot[ib] , True ) ; /* use seq->dialog      */
10113 
10114       FREE_AV( seq->mont_across_av ) ;
10115       FREE_AV( seq->mont_down_av ) ;
10116       FREE_AV( seq->mont_skip_av ) ;
10117       FREE_AV( seq->mont_gap_av ) ;
10118       FREE_AV( seq->mont_gapcolor_av ) ;
10119       FREE_AV( seq->mont_type_av ) ;
10120 
10121       seq->dialog_starter    = -1 ;
10122       seq->dont_place_dialog = 0 ;  /* 23 Jan 2004 */
10123    }
10124 
10125    EXRETURN ;
10126 }
10127 
10128 /*---------------------------------------------------------------------------
10129    Routine to get one image for the montage display.
10130    Output image is MRI_short (underlay or overlay index), or MRI_rgb.
10131 -----------------------------------------------------------------------------*/
10132 
ISQ_manufacture_one(int nim,int overlay,MCW_imseq * seq)10133 MRI_IMAGE * ISQ_manufacture_one( int nim , int overlay , MCW_imseq *seq )
10134 {
10135    MRI_IMAGE *im , *ovim , *tim ;
10136    int nrold ;
10137 
10138 ENTRY("ISQ_manufacture_one") ;
10139 
10140    if( ! ISQ_VALID(seq) ) RETURN( NULL );
10141 
10142    if( seq->mont_periodic ){
10143      while( nim < 0 )                       nim += seq->status->num_total ;
10144      while( nim >= seq->status->num_total ) nim -= seq->status->num_total ;
10145    } else {
10146      if( nim < 0 || nim >= seq->status->num_total ) RETURN( NULL );
10147    }
10148 
10149    /** Not an overlay image **/
10150 
10151    if( !overlay ){
10152      switch( seq->render_mode ){
10153        default:
10154          tim = ISQ_getimage( nim , seq ) ;
10155          if( tim == NULL ) RETURN(NULL) ;
10156          im = ISQ_process_mri( nim , seq , tim , 0 ) ; mri_free(tim) ;
10157        break ;
10158 
10159        case RENDER_WIPE_LEFT:    /* WIPE stuff 22 Aug 2014 */
10160        case RENDER_WIPE_BOT:
10161        case RENDER_MIX:
10162        case RENDER_WIPE_RIGHT:
10163        case RENDER_WIPE_TOP:
10164        case RENDER_CHECK_UO:
10165        case RENDER_CHECK_OU:
10166          im = ISQ_getchecked( nim , seq ) ;
10167          if( im == NULL ) RETURN(NULL) ;
10168        break ;
10169      }
10170      RETURN(im) ;
10171    }
10172 
10173    /** Get the overlay image **/
10174 
10175    if( ISQ_SKIP_OVERLAY(seq) ) RETURN( NULL );
10176 
10177    tim = ISQ_getoverlay( nim , seq ) ;
10178    if( tim == NULL ) RETURN( NULL );
10179    if( !ISQ_GOOD_OVERLAY_TYPE(tim->kind) ){
10180      fprintf(stderr,"\a\n*** Illegal overlay image kind=%d! ***\n",(int)tim->kind) ;
10181      mri_free(tim) ; RETURN( NULL );
10182    }
10183    ovim = mri_flippo( ISQ_TO_MRI_ROT(seq->opt.rot),seq->opt.mirror,tim ) ;
10184    if( tim != ovim ) mri_free(tim) ;
10185    ISQ_apply_mask( seq->last_automask , ovim ) ;
10186    RETURN( ovim );
10187 }
10188 
10189 /*---------------------------------------------------------------------------
10190    Routine to make a montage of images
10191    (version of ISQ_make_image when more than one is needed).
10192 -----------------------------------------------------------------------------*/
10193 
10194 #define ISQ_set_deltival(sss,dv)        \
10195   AFNI_CALL_VOID_3ARG( (sss)->getim ,   \
10196                        int,(dv), int,isqCR_deltival, XtPointer,(sss)->getaux )
10197 
ISQ_make_montage(MCW_imseq * seq)10198 void ISQ_make_montage( MCW_imseq *seq )
10199 {
10200    MRI_IMAGE *im , *ovim , *tim ;
10201    RwcBoolean reset_done = False ;
10202    float fac , wmm , hmm ;
10203    short gap_ov ;
10204    float vfac = VGFAC(seq) ;
10205 
10206    byte  gap_rgb[3] ;  /* 11 Feb 1999 */
10207    void  *gapval ;
10208    int   isrgb ;
10209    int   isrgb_ov ;    /* 07 Mar 2001 */
10210    int   div=0 ;
10211 
10212    rgba  gap_rgba ;    /* 09 Dec 2014 */
10213 
10214 ENTRY("ISQ_make_montage");
10215 
10216    if( ! ISQ_VALID(seq) ) EXRETURN ;
10217 
10218    KILL_2XIM( seq->given_xim , seq->sized_xim ) ;  /* erase the XImages */
10219 
10220    if( seq->mplot != NULL ){                           /* 19 Sep 2001 */
10221      delete_memplot( seq->mplot ) ; seq->mplot = NULL ;
10222    }
10223 
10224    /*-- process toggled options that affect the image that may be stored --*/
10225 
10226    if( seq->opt.rot         != seq->old_opt.rot         ||
10227        seq->opt.mirror      != seq->old_opt.mirror      ||
10228        seq->opt.scale_group != seq->old_opt.scale_group ||
10229        seq->opt.scale_range != seq->old_opt.scale_range ||
10230        seq->mont_nx         != seq->mont_nx_old         ||
10231        seq->mont_ny         != seq->mont_ny_old         ||
10232        seq->mont_skip       != seq->mont_skip_old         ){
10233 
10234       KILL_1MRI( seq->imim ) ;  /* must re-get image for new processing */
10235       KILL_1MRI( seq->ovim ) ;
10236    }
10237 
10238    /*--- set the image to process ---*/
10239 
10240    im = seq->imim ;
10241 
10242    if( im == NULL ){
10243       float new_width_mm = 0.0 , new_height_mm = 0.0 ;
10244       int   nxim = 0 , nyim = 0 , nxyim = 0 ;
10245       int ij , nim , nmont = seq->mont_nx * seq->mont_ny , ijcen ;
10246       MRI_IMARR *mar ;
10247 
10248       INIT_IMARR(mar) ;
10249 
10250       /** Compute ijcen = montage index of subimage that will
10251                           be the "center" (crosshairs, etc.).
10252           N.B.: If the algorithm for this is changed here, it
10253                 must be changed in a number of other places,
10254                 including the AFNI multiple crosshairs code! **/
10255 
10256 if( AFNI_yesenv("TMONT") )
10257 INFO_message("Start Montagizing") ;
10258       isrgb = 0 ;
10259       ijcen = (seq->mont_nx)/2 + (seq->mont_ny/2) * seq->mont_nx ;
10260       for( ij=0 ; ij < nmont ; ij++ ){  /* loop to get all montage underlays */
10261          if( seq->mont_mode > 0 ){
10262            nim = seq->im_nr ;
10263            div = (seq->mont_skip + 1) * (ij - ijcen) ;
10264 if( AFNI_yesenv("TMONT") )
10265 ININFO_message("set deltival=%d  nim=%d",div,nim) ;
10266            ISQ_set_deltival( seq , div ) ;
10267          } else {
10268            nim = seq->im_nr + (seq->mont_skip + 1) * (ij - ijcen) ;
10269          }
10270 
10271          seq->set_orim = (seq->need_orim != 0 && nim == seq->im_nr && div == 0) ;
10272          tim = ISQ_manufacture_one( nim , 0 , seq ) ;
10273          seq->set_orim = 0 ;
10274          ADDTO_IMARR(mar,tim) ;
10275 
10276          if( nim == seq->im_nr && div == 0 && tim != NULL ){
10277             new_width_mm  = IM_WIDTH(tim)  ; nxim = tim->nx ;
10278             new_height_mm = IM_HEIGHT(tim) ; nyim = tim->ny ;
10279             seq->last_image_type = tim->kind ;
10280             seq->barbot = seq->clbot ; /* 29 Jul 2001 */
10281             seq->bartop = seq->cltop ;
10282             ISQ_set_barhint(seq,"Focus") ;
10283             seq->last_dx = fabs(tim->dx) ; seq->last_dy = fabs(tim->dy) ;
10284          }
10285 
10286          if( tim != NULL ){
10287             isrgb = isrgb || (tim != NULL && tim->kind == MRI_rgb) ;
10288             nxyim++ ;
10289          }
10290       }
10291       if( seq->mont_mode > 0 ){ div = 0; ISQ_set_deltival(seq,div); }
10292 
10293       if( nxyim == 0 ){                                /* bad bad bad bad bad */
10294          fprintf(stderr,"** Montage error: no images found!\n") ;
10295          DESTROY_IMARR(mar) ; EXRETURN ;
10296       }
10297 
10298       if( isrgb ){                       /* 11 Feb 1999 */
10299          if( seq->mont_gapcolor > 0 )
10300             DC_pixel_to_rgb( seq->dc , seq->dc->ovc->pix_ov[seq->mont_gapcolor],
10301                              gap_rgb , gap_rgb+1 , gap_rgb+2 ) ;
10302          else
10303             gap_rgb[0] = gap_rgb[1] = gap_rgb[2] = 0 ;
10304 
10305          gapval = (void *) gap_rgb ;
10306       } else {
10307          gap_ov = -(seq->mont_gapcolor) ;  /* negative ==> overlay palette */
10308          gapval = (void *) &gap_ov ;
10309       }
10310 
10311       /* 17 Feb 1999: if any are rgb, must convert all to that format */
10312 
10313       if( isrgb ){
10314          for( ij=0 ; ij < nmont ; ij++ ){
10315             tim = IMARR_SUBIMAGE(mar,ij) ;
10316             if( tim != NULL && tim->kind != MRI_rgb ){
10317                MRI_IMAGE *qim ;
10318 
10319                if( tim->kind == MRI_short )
10320                   qim = ISQ_index_to_rgb( seq->dc , 0 , tim ) ; /* 07 Mar 2001 */
10321                else
10322                   qim = mri_to_rgb( tim ) ;                     /* the old way */
10323 
10324                mri_free(tim) ;                   /* replace in image array */
10325                IMARR_SUBIMAGE(mar,ij) = qim ;
10326             }
10327          }
10328       }
10329 
10330       /* put them all together into one honking image (short or rgb)! */
10331 
10332       seq->imim = im = mri_cat2D( seq->mont_nx , seq->mont_ny ,     /* save this */
10333                                   seq->mont_gap , gapval , mar ) ;  /* underlay  */
10334 
10335 STATUS("Destroying underlay image array") ;
10336 
10337       DESTROY_IMARR(mar) ;
10338 
10339       /* fix window dimensions if individual image size is different */
10340 
10341       seq->horig = nxim ; seq->vorig = nyim ;
10342 
10343       wmm = ( nxim*seq->mont_nx + seq->mont_gap*(seq->mont_nx-1) )
10344            / (float) nxim ;
10345 
10346       hmm = ( nyim*seq->mont_ny + seq->mont_gap*(seq->mont_ny-1) )
10347            / (float) nyim ;
10348 
10349       fac = sqrt( wmm / hmm ) ;
10350 
10351       new_width_mm  *= fac ;
10352       new_height_mm /= fac ;
10353 
10354       if( FLDIF(new_width_mm ,seq->last_width_mm ) ||
10355           FLDIF(new_height_mm,seq->last_height_mm)   ){
10356 
10357          ISQ_reset_dimen( seq , new_width_mm , new_height_mm ) ;
10358          reset_done = True ;
10359       }
10360    }
10361 
10362    /** at this point, im contains the underlay image, which may be short or rgb **/
10363 
10364    if( seq->opt.free_aspect != seq->old_opt.free_aspect && !reset_done )
10365       ISQ_reset_dimen( seq , seq->last_width_mm , seq->last_height_mm ) ;
10366 
10367    /*--- set the overlay to process ---*/
10368 
10369    if( ISQ_SKIP_OVERLAY(seq) ){
10370       KILL_1MRI( seq->ovim ) ; ovim = NULL ;  /* that was easy */
10371    } else {
10372       int ij , nim , nmont=seq->mont_nx * seq->mont_ny , nov=0 , ijcen ;
10373 
10374       MEM_plotdata *mp ; /* 19 Sep 2001 */
10375       int ii,jj ;
10376       float sx,sy,st , xb,xt,yb,yt , tx,ty ;
10377 
10378       ijcen = (seq->mont_nx)/2 + (seq->mont_ny/2) * seq->mont_nx ;
10379 
10380       /*--- get overlay images and montage them, if needed ---*/
10381 
10382       ovim = seq->ovim ;
10383       if( ovim == NULL ){
10384          MRI_IMARR *mar ;
10385 
10386          INIT_IMARR(mar) ;
10387 
10388          isrgb_ov = 0 ;  /* 07 Mar 2001 */
10389 
10390          for( ij=0 ; ij < nmont ; ij++ ){
10391             if( seq->mont_mode > 0 ){
10392               nim = seq->im_nr ;
10393               div = (seq->mont_skip + 1) * (ij - ijcen) ;
10394               ISQ_set_deltival( seq , div ) ;
10395             } else {
10396               nim = seq->im_nr + (seq->mont_skip + 1) * (ij - ijcen) ;
10397             }
10398 
10399             tim = ISQ_manufacture_one( nim , 1 , seq ) ;
10400             ADDTO_IMARR(mar,tim) ;
10401             if( tim != NULL ){
10402                nov++ ;
10403                     if( tim->kind == MRI_rgb  && isrgb_ov == 0 ) isrgb_ov = 1 ;
10404                else if( tim->kind == MRI_rgba                  ) isrgb_ov = 2 ;
10405             }
10406          }
10407          if( seq->mont_mode > 0 ){ div = 0; ISQ_set_deltival(seq,div); }
10408 
10409          /* 07 Mar 2001: deal with possible RGB overlays */
10410 
10411          if( isrgb_ov == 1 ){
10412             for( ij=0 ; ij < nmont ; ij++ ){
10413                tim = IMARR_SUBIMAGE(mar,ij) ;
10414                if( tim != NULL && tim->kind != MRI_rgb ){
10415                   MRI_IMAGE *qim ;
10416 
10417                   if( tim->kind == MRI_short )
10418                      qim = ISQ_index_to_rgb( seq->dc , 1 , tim ) ; /* 07 Mar 2001 */
10419                   else
10420                      qim = mri_to_rgb( tim ) ;                     /* the old way */
10421 
10422                   mri_free(tim) ;                   /* replace in image array */
10423                   IMARR_SUBIMAGE(mar,ij) = qim ;
10424                }
10425             }
10426          } else if( isrgb_ov == 2 ){                /* 09 Dec 2014 */
10427            for( ij=0 ; ij < nmont ; ij++ ){
10428              tim = IMARR_SUBIMAGE(mar,ij) ;
10429              if( tim != NULL && tim->kind != MRI_rgba ){
10430                MRI_IMAGE *qim , *zim ;
10431 
10432                if( tim->kind == MRI_short ){
10433                   zim = ISQ_index_to_rgb( seq->dc , 1 , tim ) ;
10434                   qim = mri_to_rgba(zim) ; mri_free(zim) ;
10435                } else {
10436                   qim = mri_to_rgba(tim) ;
10437                }
10438 
10439                mri_free(tim) ; IMARR_SUBIMAGE(mar,ij) = qim ;
10440              }
10441            }
10442          }
10443 
10444          if( isrgb_ov == 1 ){
10445             gap_rgb[0] = gap_rgb[1] = gap_rgb[2] = 0 ;
10446             gapval = (void *)gap_rgb ;
10447          } else if( isrgb_ov == 2 ){     /* 09 Dec 2014 */
10448             gap_rgba.r = gap_rgba.g = gap_rgba.b = gap_rgba.a = 0 ;
10449             gapval = (void *)(&gap_rgba) ;
10450          } else {
10451             gap_ov = 0 ;
10452             gapval = (void *)(&gap_ov) ;
10453          }
10454 
10455          if( nov > 0 ){
10456             ovim = seq->ovim =                                /* save this */
10457                mri_cat2D( seq->mont_nx , seq->mont_ny ,       /* overlay   */
10458                           seq->mont_gap , gapval ,  mar ) ;
10459          } else
10460             ovim = seq->ovim = NULL ;                         /* nothing */
10461 
10462 STATUS("Destroying overlay image array") ;
10463 
10464          DESTROY_IMARR( mar ) ;
10465       }
10466 
10467       /*--- 19 Sep 2001: make overlay line plots for image? ---*/
10468 
10469       /*-- get sub-plots for each sub-image,
10470            merge into a superplot for the montage --*/
10471 
10472       if( MCW_val_bbox(seq->wbar_plots_bbox) != 0 ){
10473        for( ij=0 ; ij < nmont ; ij++ ){
10474 
10475          nim = seq->im_nr + (seq->mont_skip + 1) * (ij - ijcen) ;
10476          if( seq->mont_periodic ){
10477             while( nim < 0 )                       nim += seq->status->num_total ;
10478             while( nim >= seq->status->num_total ) nim -= seq->status->num_total ;
10479          } else {
10480             if( nim < 0 || nim >= seq->status->num_total ) continue ; /* skip */
10481          }
10482 
10483          mp = ISQ_getmemplot( nim , seq ) ;
10484 
10485          if( mp == NULL ) continue ; /* skip */
10486 
10487          ii = ij % seq->mont_nx ;  /* sub-image x index in montage */
10488          jj = ij / seq->mont_nx ;  /* sub-image y index in montage */
10489 
10490          tx = im->nx ; ty = im->ny ;  /* size of underlay image */
10491 
10492          /* sub-image is inside (xb..xt) X (yb..yt) in
10493             plot coordinates -- y is down-to-up,
10494             whereas image coordinates run y is up-to-down */
10495 
10496          xb = (seq->horig + seq->mont_gap) * ii ;
10497          xt = xb + seq->horig ;
10498          yb = (seq->vorig + seq->mont_gap) * (seq->mont_ny - 1 - jj) ;
10499          yt = yb + seq->vorig ;
10500 
10501          /* scale factors to put this sub-plot
10502             in the correct place in the montage */
10503 
10504          sx = (xt-xb) / tx ; tx = xb / tx ;
10505          sy = (yt-yb) / ty ; ty = yb / ty ;  st = sqrt(sx*sy) ;
10506 
10507          /* rotate/flip to same orientation as sub-image */
10508 
10509          flip_memplot( ISQ_TO_MRI_ROT(seq->opt.rot),seq->opt.mirror, mp ) ;
10510 
10511          /* scale to correct location as sub-image */
10512 
10513          scale_memplot( sx,tx , sy,ty , st , mp ) ;
10514 
10515          /* attach to superplot */
10516 
10517          if( seq->mplot == NULL ){  /* make 1st one the superplot */
10518            seq->mplot = mp ;
10519          } else {                  /* attach later ones to superplot */
10520            append_to_memplot( seq->mplot , mp ) ;
10521            delete_memplot( mp ) ;
10522          }
10523 
10524        } /* end of loop over sub-images' sub-plots */
10525       } /* end of if over whether to plot the plot */
10526 
10527       /*--- 20 Sep 2001: plot labels ---*/
10528 
10529       if( seq->wbar_label_av->ival != 0 ){
10530        char *lab ;
10531 
10532        for( ij=0 ; ij < nmont ; ij++ ){
10533 
10534          nim = seq->im_nr + (seq->mont_skip + 1) * (ij - ijcen) ;
10535          if( seq->mont_periodic ){
10536             while( nim < 0 )                       nim += seq->status->num_total ;
10537             while( nim >= seq->status->num_total ) nim -= seq->status->num_total ;
10538          } else {
10539             if( nim < 0 || nim >= seq->status->num_total ) continue ; /* skip */
10540          }
10541 
10542          /*- get label string -*/
10543 
10544          lab = ISQ_getlabel( nim , seq ) ;
10545          if( lab != NULL ){
10546           mp = ISQ_plot_label( seq , lab ) ;  /* plot it */
10547           if( mp != NULL ){
10548            ii = ij % seq->mont_nx ;  /* sub-image x index in montage */
10549            jj = ij / seq->mont_nx ;  /* sub-image y index in montage */
10550            tx = im->nx ; ty = im->ny ;  /* size of underlay image */
10551            xb = (seq->horig + seq->mont_gap) * ii ;
10552            xt = xb + seq->horig ;
10553            yb = (seq->vorig + seq->mont_gap) * (seq->mont_ny - 1 - jj) ;
10554            yt = yb + seq->vorig ;
10555            sx = (xt-xb) / tx ; tx = xb / tx ;
10556            sy = (yt-yb) / ty ; ty = yb / ty ;  st = sqrt(sx*sy) ;
10557            scale_memplot( sx,tx , sy,ty , st , mp ) ;
10558            if( seq->mplot != NULL ){
10559              append_to_memplot( seq->mplot , mp ) ; delete_memplot( mp ) ;
10560            } else {
10561              seq->mplot = mp ;
10562            }
10563           }
10564           free(lab) ;
10565          }
10566        } /* end of loop over sub-images */
10567       } /* end of plot labels */
10568 
10569    } /* end of making overlay stuff */
10570 
10571    /*--- set old_opt to current options ---*/
10572 
10573    seq->old_opt = seq->opt ;
10574 
10575    seq->mont_nx_old        = seq->mont_nx        ;
10576    seq->mont_ny_old        = seq->mont_ny        ;
10577    seq->mont_skip_old      = seq->mont_skip      ;
10578    seq->mont_gap_old       = seq->mont_gap       ;
10579    seq->mont_gapcolor_old  = seq->mont_gapcolor  ;
10580 
10581    /*--- overlay ovim onto im, producing tim, if needed ---*/
10582 
10583    if( ovim == NULL || ISQ_SKIP_OVERLAY(seq) ){   /* no processing of overlay */
10584       tim = im ;
10585    } else {
10586       tim = ISQ_overlay( seq->dc , im, ovim, seq->ov_opacity ) ;
10587       if( tim == NULL ) tim = im ;     /* shouldn't happen */
10588    }
10589 
10590    if( vfac > 0.0f ){
10591      MRI_IMAGE *qim ;
10592      MCW_invert_widget(seq->wbut_bot[NBUT_DISP]) ;
10593      vgize_sigfac = vfac ; qim = mri_vgize(tim) ;
10594      MCW_invert_widget(seq->wbut_bot[NBUT_DISP]) ;
10595      if( qim != NULL ){
10596        if( tim != im ) KILL_1MRI(tim);
10597        tim = qim;
10598      }
10599    }
10600 
10601    /*--- convert result to XImage for display ---*/
10602 
10603    seq->given_xim = mri_to_XImage( seq->dc , tim ) ;
10604 
10605    if( tim != im ) KILL_1MRI(tim) ;
10606    EXRETURN ;
10607 }
10608 
10609 /*----------------------------------------------------------------------
10610     Given a pair of coordinates in the image window, find the original
10611     coordinates they come from in the image, allowing for rotations,
10612     mirroring, and montaging.  This new version (April 1996) also
10613     returns the image number that the coordinates occurred in, since
10614     that may vary with montaging.
10615 
10616     12 Mar 2002: modified to allow for possibility of zoom
10617     12 Jun 2002: and to allow for cropping
10618 -------------------------------------------------------------------------*/
10619 
ISQ_mapxy(MCW_imseq * seq,int xwin,int ywin,int * xim,int * yim,int * nim)10620 void ISQ_mapxy( MCW_imseq *seq, int xwin, int ywin,
10621                 int *xim, int *yim, int *nim )
10622 {
10623    int win_wide,win_high , nxim,nyim ;
10624    int monx,mony,monsk,mongap , win_wide_orig,win_high_orig ;
10625    int xorg , yorg , ijcen , xcol,yrow , ij ;
10626    int zlev = seq->zoom_fac ;
10627 
10628 ENTRY("ISQ_mapxy") ;
10629 
10630    if( ! ISQ_REALZ(seq) ) EXRETURN ;
10631 
10632    nxim  = seq->horig     ; nyim   = seq->vorig    ;  /* sizes of original images */
10633    monx  = seq->mont_nx   ; mony   = seq->mont_ny  ;  /* montage layout parameters */
10634    monsk = seq->mont_skip ; mongap = seq->mont_gap ;
10635 
10636    win_wide_orig = nxim * monx + mongap * (monx-1) ;  /* un-resized (original) */
10637    win_high_orig = nyim * mony + mongap * (mony-1) ;  /* displayed image sizes */
10638 
10639    /* get actual (display) image sizes */
10640 
10641    if( seq->wimage_width <= 0 ){
10642       MCW_widget_geom( seq->wimage , &win_wide , &win_high , NULL,NULL ) ;
10643       seq->wimage_width  = win_wide ;
10644       seq->wimage_height = win_high ;
10645    } else {
10646       win_wide = seq->wimage_width ;
10647       win_high = seq->wimage_height ;
10648    }
10649 
10650    /* convert actual coordinates input to
10651       equivalent coordinates in the original (montaged) image */
10652 
10653    /* conversion if zoom is not on */
10654 
10655    if( zlev == 1 || monx > 1 || mony > 1 ){
10656 
10657      xorg = ( (float) xwin / win_wide ) * win_wide_orig /* + 0.49 */ ;
10658      yorg = ( (float) ywin / win_high ) * win_high_orig /* + 0.49 */ ;
10659 
10660    } else {  /* conversion if zoom is on (only in 1x1 montages) */
10661 
10662      int pw=seq->zoom_pw , ph=seq->zoom_ph ;
10663      float xoff,yoff ;
10664 
10665      xoff = seq->zoom_hor_off*pw; if( xoff+win_wide > pw ) xoff = pw-win_wide;
10666      yoff = seq->zoom_ver_off*ph; if( yoff+win_high > ph ) yoff = ph-win_high;
10667 
10668      xorg = nxim * (xoff+xwin) / pw ;
10669      yorg = nyim * (yoff+ywin) / ph ;
10670    }
10671 
10672    /* compute the coordinates within the sub-image (*xim and *yim),
10673       and the grid column and row number of the sub-image (xcol,yrow) */
10674 
10675    *xim = xorg % (nxim+mongap) ; xcol = xorg / (nxim+mongap) ;
10676    *yim = yorg % (nyim+mongap) ; yrow = yorg / (nyim+mongap) ;
10677 
10678    /* compute the image number in the sequence that (xcol,yrow)
10679       came from, using the same algorithm as in ISQ_make_montage */
10680 
10681    ij    = xcol   + yrow     * monx ;
10682    ijcen = monx/2 + (mony/2) * monx ;
10683    *nim  = seq->im_nr + (monsk+1) * (ij-ijcen) ;
10684 
10685    if( seq->mont_periodic ){
10686       while( *nim < 0 )                       *nim += seq->status->num_total ;
10687       while( *nim >= seq->status->num_total ) *nim -= seq->status->num_total ;
10688    }
10689 
10690    /* flip the (xim,yim) coordinates in case the stupid user used
10691       one of the rotate or mirror buttons in the "Disp" control box */
10692 
10693    ISQ_flipxy( seq , xim , yim ) ;
10694 
10695    if( seq->cropit ){       /* 12 Jun 2002: allow for cropping */
10696      *xim += seq->crop_xa ;
10697      *yim += seq->crop_ya ;
10698    }
10699 
10700    EXRETURN ;
10701 }
10702 
10703 /*---------------------------------------------------------------------
10704    Inputs: xflip,yflip = pointers to coordinates in the flipped image
10705    Output: xflip,yflip = values are changed to original image coords
10706 
10707    Note that these coordinates are relative to original (un-resized)
10708    image dimensions.
10709 
10710    Also see ISQ_unflipxy().
10711 -----------------------------------------------------------------------*/
10712 
ISQ_flipxy(MCW_imseq * seq,int * xflip,int * yflip)10713 void ISQ_flipxy( MCW_imseq *seq, int *xflip, int *yflip )
10714 {
10715    int fopt , xim , yim , nx,ny ;
10716 
10717 ENTRY("ISQ_flipxy") ;
10718 
10719    fopt = ISQ_TO_MRI_ROT(seq->opt.rot) ;
10720    if( seq->opt.mirror ) fopt += MRI_FLMADD ;
10721 
10722    nx = seq->horig ; ny = seq->vorig ;
10723 
10724    switch( fopt ){
10725 
10726       default:                                    /* ROT_0, no mirror */
10727       case (MRI_ROT_0):
10728          xim = *xflip ; yim = *yflip ; break ;
10729 
10730       case (MRI_ROT_90):                          /* ROT_90, no mirror */
10731          xim = ny-1-*yflip ; yim = *xflip ; break ;
10732 
10733       case (MRI_ROT_180):                         /* ROT_180, no mirror */
10734          xim = nx-1-*xflip ; yim = ny-1-*yflip ; break ;
10735 
10736       case (MRI_ROT_270):                         /* ROT_270, no mirror */
10737          xim = *yflip ; yim = nx-1-*xflip ; break ;
10738 
10739       case (MRI_ROT_0+MRI_FLMADD):                /* ROT_0, mirror */
10740          xim = nx-1-*xflip ; yim = *yflip ; break ;
10741 
10742       case (MRI_ROT_90+MRI_FLMADD):               /* ROT_90, mirror */
10743          xim = ny-1-*yflip ; yim = nx-1-*xflip ; break ;
10744 
10745       case (MRI_ROT_180+MRI_FLMADD):              /* ROT_180, mirror */
10746          xim = *xflip ; yim = ny-1-*yflip ; break ;
10747 
10748       case (MRI_ROT_270+MRI_FLMADD):              /* ROT_270, mirror */
10749          xim = *yflip ; yim = *xflip ; break ;
10750    }
10751 
10752    *xflip = xim ; *yflip = yim ; EXRETURN ;
10753 }
10754 
10755 /*-------------------------------------------------------------------*/
10756 /* The inverse to ISQ_flipxy() */
10757 
ISQ_unflipxy(MCW_imseq * seq,int * xflip,int * yflip)10758 void ISQ_unflipxy( MCW_imseq *seq, int *xflip, int *yflip )
10759 {
10760    int fopt , xim , yim , nx,ny ;
10761 
10762 ENTRY("ISQ_unflipxy") ;
10763 
10764    fopt = ISQ_TO_MRI_ROT(seq->opt.rot) ;
10765    if( seq->opt.mirror ) fopt += MRI_FLMADD ;
10766 
10767    nx = seq->horig ; ny = seq->vorig ;
10768 
10769    switch( fopt ){
10770 
10771       default:                                    /* ROT_0, no mirror */
10772       case (MRI_ROT_0):
10773          xim = *xflip ; yim = *yflip ; break ;
10774 
10775       case (MRI_ROT_90):                          /* ROT_90, no mirror */
10776          yim = ny-1-*xflip ; xim = *yflip ; break ;
10777 
10778       case (MRI_ROT_180):                         /* ROT_180, no mirror */
10779          xim = nx-1-*xflip ; yim = ny-1-*yflip ; break ;
10780 
10781       case (MRI_ROT_270):                         /* ROT_270, no mirror */
10782          yim = *xflip ; xim = nx-1-*yflip ; break ;
10783 
10784       case (MRI_ROT_0+MRI_FLMADD):                /* ROT_0, mirror */
10785          xim = nx-1-*xflip ; yim = *yflip ; break ;
10786 
10787       case (MRI_ROT_90+MRI_FLMADD):               /* ROT_90, mirror */
10788          yim = ny-1-*xflip ; xim = nx-1-*yflip ; break ;
10789 
10790       case (MRI_ROT_180+MRI_FLMADD):              /* ROT_180, mirror */
10791          xim = *xflip ; yim = ny-1-*yflip ; break ;
10792 
10793       case (MRI_ROT_270+MRI_FLMADD):              /* ROT_270, mirror */
10794          xim = *yflip ; yim = *xflip ; break ;
10795    }
10796 
10797    *xflip = xim ; *yflip = yim ; EXRETURN ;
10798 }
10799 
10800 /*-----------------------------------------------------------------------------
10801    Routines to handle transformations of an image.
10802 -------------------------------------------------------------------------------*/
10803 
ISQ_transform_label(MCW_arrowval * av,XtPointer cd)10804 char * ISQ_transform_label( MCW_arrowval *av , XtPointer cd )
10805 {
10806    MCW_function_list *xforms = (MCW_function_list *) cd ;
10807 
10808    if( av == NULL    || xforms == NULL        ||
10809        av->ival <= 0 || av->ival > xforms->num  ) return "-none-" ;
10810 
10811    return xforms->labels[av->ival - 1] ;  /* label for each function */
10812 }
10813 
10814 /*-----------------------------------------------------------------------------*/
10815 
ISQ_transform_CB(MCW_arrowval * av,XtPointer cd)10816 void ISQ_transform_CB( MCW_arrowval *av , XtPointer cd )
10817 {
10818    MCW_imseq *seq = (MCW_imseq *) cd ;
10819 
10820 ENTRY("ISQ_transform_CB") ;
10821 
10822    if( ! ISQ_VALID(seq) ) EXRETURN ;
10823 
10824    /** set the 0D transform function pointer **/
10825 
10826    if( av != NULL && av == seq->transform0D_av ){
10827       if( seq->status->transforms0D == NULL || av->ival <= 0 ||
10828           av->ival > seq->status->transforms0D->num            ){
10829 
10830          seq->transform0D_func  = NULL ;  /* no transform */
10831          seq->transform0D_index = 0 ;
10832       } else {
10833          seq->transform0D_func  = seq->status->transforms0D->funcs[av->ival - 1] ;
10834          seq->transform0D_index = av->ival ;
10835 
10836          /* 21 Jul 2003: do initializing func call, if present */
10837 
10838          if( seq->status->transforms0D->func_init[av->ival-1] != NULL )
10839           seq->status->transforms0D->func_init[av->ival-1]() ;
10840 
10841       }
10842    }
10843 
10844    /** set the 2D transform function pointer **/
10845 
10846    if( av != NULL && av == seq->transform2D_av ){
10847       if( seq->status->transforms2D == NULL || av->ival <= 0 ||
10848           av->ival > seq->status->transforms2D->num            ){
10849 
10850          seq->transform2D_func  = NULL ;  /* no transform */
10851          seq->transform2D_index = 0 ;
10852       } else {
10853          seq->transform2D_func  = seq->status->transforms2D->funcs[av->ival - 1] ;
10854          seq->transform2D_index = av->ival ;
10855 
10856          /* 21 Jul 2003: do initializing func call, if present */
10857 
10858          if( seq->status->transforms2D->func_init[av->ival-1] != NULL )
10859           seq->status->transforms2D->func_init[av->ival-1]() ;
10860       }
10861    }
10862 
10863    ISQ_redisplay( seq , -1 , isqDR_reimage ) ;  /* redo current image */
10864    EXRETURN ;
10865 }
10866 
10867 /*-----------------------------------------------------------------------------*/
10868 
ISQ_slice_proj_CB(MCW_arrowval * av,XtPointer cd)10869 void ISQ_slice_proj_CB( MCW_arrowval *av , XtPointer cd )
10870 {
10871    MCW_imseq *seq = (MCW_imseq *) cd ;
10872 
10873 ENTRY("ISQ_slice_proj_CB") ;
10874 
10875    if( ! ISQ_VALID(seq) ) EXRETURN ;
10876 
10877    /** set the slice_proj function pointer **/
10878 
10879    if( av != NULL && av == seq->slice_proj_av ){
10880       if( seq->status->slice_proj == NULL || av->ival <= 0 ||
10881           av->ival > seq->status->slice_proj->num            ){
10882 
10883          seq->slice_proj_func  = NULL ;  /* no slice_proj */
10884          seq->slice_proj_index = 0 ;
10885       } else {
10886          seq->slice_proj_func  = (float_func *)
10887                                  seq->status->slice_proj->funcs[av->ival - 1] ;
10888          seq->slice_proj_index = av->ival ;
10889       }
10890    }
10891 
10892    seq->slice_proj_range = seq->slice_proj_range_av->ival ;
10893 
10894    ISQ_redisplay( seq , -1 , isqDR_reimage ) ;  /* redo current image */
10895    EXRETURN ;
10896 }
10897 
10898 /*--------------------------------------------------------------------------
10899    30 Dec 1998:  Handle the row graphs
10900 ----------------------------------------------------------------------------*/
10901 
ISQ_rowgraph_label(MCW_arrowval * av,XtPointer cd)10902 char * ISQ_rowgraph_label( MCW_arrowval *av , XtPointer cd )
10903 {
10904    static char buf[16] ;
10905    sprintf(buf,"%2d  ",av->ival) ;
10906    return buf ;
10907 }
10908 
10909 /*--------------------------------------------------------------------------*/
10910 
ISQ_rowgraph_CB(MCW_arrowval * av,XtPointer cd)10911 void ISQ_rowgraph_CB( MCW_arrowval *av , XtPointer cd )
10912 {
10913    MCW_imseq *seq = (MCW_imseq *) cd ;
10914 
10915 ENTRY("ISQ_rowgraph_CB") ;
10916 
10917    if( ! ISQ_VALID(seq) ) EXRETURN ;               /* bad input */
10918    if( av->ival == seq->rowgraph_num ) EXRETURN ;  /* nothing changed */
10919 
10920    seq->rowgraph_num = av->ival ;
10921 
10922    if( seq->rowgraph_num > 0 ) seq->need_orim |=  ROWGRAPH_MASK ;
10923    else                        seq->need_orim &= ~ROWGRAPH_MASK ;
10924    if( seq->need_orim == 0 ) KILL_1MRI(seq->orim) ;
10925 
10926    ISQ_redisplay( seq , -1 , isqDR_reimage ) ;  /* redo current image */
10927    EXRETURN ;
10928 }
10929 
10930 /*--------------------------------------------------------------------------*/
10931 
ISQ_rowgraph_draw(MCW_imseq * seq)10932 void ISQ_rowgraph_draw( MCW_imseq *seq )
10933 {
10934    MEM_plotdata *mp ;
10935    ISQ_cbs cbs ;
10936    int jbot,ix,jy , nrow , jj , nx,ny , ymask ;
10937    float *yar[ROWGRAPH_MAX] ;
10938 
10939 ENTRY("ISQ_rowgraph_draw") ;
10940 
10941    if( ! ISQ_REALZ(seq) ) EXRETURN ;  /* error */
10942 
10943    /* marked for no graphs? */
10944 
10945    if( seq->rowgraph_num == 0 ){
10946      if( seq->rowgraph_mtd != NULL ){
10947        plotkill_topshell( seq->rowgraph_mtd ) ;
10948        seq->rowgraph_mtd = NULL ;
10949      }
10950      EXRETURN ;
10951    }
10952 
10953    if( seq->orim == NULL ) EXRETURN ;
10954 
10955    /* find current location of crosshairs (if any) */
10956 
10957    cbs.reason = isqCR_getxynim ;
10958    cbs.xim = cbs.yim = cbs.nim = -666 ;
10959    if( seq->status->send_CB != NULL )
10960 #if 0
10961      seq->status->send_CB( seq , seq->getaux , &cbs ) ;
10962 #else
10963      SEND(seq,cbs) ;
10964 #endif
10965    if( cbs.xim < 0 || cbs.yim < 0 ){
10966      ERROR_message("in ISQ_rowgraph_draw: xim=%d yim=%d",cbs.xim,cbs.yim) ;
10967      EXRETURN ;  /* bad result */
10968    }
10969    ISQ_unflipxy( seq , &(cbs.xim) , &(cbs.yim) ) ;
10970    jy = jbot = cbs.yim ; ix = cbs.xim ;
10971 
10972    /* get pointers to data rows */
10973 
10974    if( jbot < 0 || jbot >= seq->orim->ny ){
10975       ERROR_message("in ISQ_rowgraph_draw: jbot=%d",jbot) ;
10976       EXRETURN ;  /* no data? */
10977    }
10978 
10979    nrow = MIN( seq->rowgraph_num  , jbot+1 ) ;
10980    nx   = seq->orim->nx ;
10981    ny   = seq->orim->ny ;
10982 
10983    for( jj=0 ; jj < nrow ; jj++ ){
10984      yar[jj] = MRI_FLOAT_PTR(seq->orim) + (jbot-jj)*nx ;
10985      (void)thd_floatscan( nx , yar[jj] ) ;
10986    }
10987 
10988    /* make a plot in memory */
10989 
10990    ymask = TSP_SEPARATE_YBOX ;
10991 
10992    plot_ts_setTHIK(0.0034567f) ;
10993    mp = plot_ts_mem( nx , NULL , nrow,ymask,yar , "Column (pixels)",NULL,NULL,NULL ) ;
10994    if( mp == NULL ){
10995       ERROR_message("in ISQ_rowgraph_draw: can't make plot_ts_mem") ;
10996       EXRETURN ;  /* error */
10997    }
10998 
10999    /*-- plot a * at the selected point (if it is in range) --*/
11000 
11001    if( !ISQ_SKIP_OVERLAY(seq) && ix >= 0 && ix < nx && jy >= 0 && jy < ny ){
11002       float xx , yy , dx , dy , xbot,xtop, ybot,ytop ;
11003 
11004       xx = ix ; dx = 0.016 * nx ; yy = yar[0][ix] ;
11005 #if 0
11006       ybot = ytop = yar[0][0] ;
11007       for( jj=1 ; jj < nx ; jj++ )
11008               if( yar[0][jj] < ybot ) ybot = yar[0][jj] ;
11009          else if( yar[0][jj] > ytop ) ytop = yar[0][jj] ;
11010       dy = 0.016 * nrow * (ytop-ybot) ;
11011 #else
11012       plotpak_getset( NULL,NULL,NULL,NULL , &xbot,&xtop , &ybot,&ytop ) ;
11013       dx = 0.016 * fabs(xtop-xbot) ;
11014       dy = 0.016 * fabs(ytop-ybot) * nrow ;
11015 #endif
11016 
11017 #undef  THIK
11018 #define THIK 0.004
11019 
11020       set_color_memplot( 0.8 , 0.0 , 0.2 ) ;
11021       set_thick_memplot( THIK ) ;
11022       plotpak_line( xx-dx , yy    , xx+dx , yy    ) ; /* - stroke */
11023       plotpak_line( xx    , yy-dy , xx    , yy+dy ) ; /* | stroke */
11024       plotpak_line( xx-dx , yy-dy , xx+dx , yy+dy ) ; /* / stroke */
11025       plotpak_line( xx+dx , yy-dy , xx-dx , yy+dy ) ; /* \ stroke */
11026       set_color_memplot( 0.2 , 0.0 , 0.8 ) ;
11027       plotpak_line( xx+dx , yy-dy , xx+dx , yy+dy ) ; /* box around outside */
11028       plotpak_line( xx+dx , yy+dy , xx-dx , yy+dy ) ;
11029       plotpak_line( xx-dx , yy+dy , xx-dx , yy-dy ) ;
11030       plotpak_line( xx-dx , yy-dy , xx+dx , yy-dy ) ;
11031       set_color_memplot( 0.0 , 0.0 , 0.0 ) ;
11032       set_thick_memplot( 0.0 ) ;
11033    }
11034 
11035    /* if there is a plot window open, plot into it, otherwise open a new window */
11036 
11037    if( seq->rowgraph_mtd != NULL ){
11038 
11039       MTD_replace_plotdata( seq->rowgraph_mtd , mp ) ;
11040       redraw_topshell( seq->rowgraph_mtd ) ;
11041 
11042    } else {  /* make a new plot window */
11043 
11044       X11_SET_NEW_PLOT ;
11045       seq->rowgraph_mtd = memplot_to_topshell( seq->dc->display, mp,
11046                                                ISQ_rowgraph_mtdkill ) ;
11047 
11048       if( seq->rowgraph_mtd == NULL ){ delete_memplot( mp ); EXRETURN; }
11049 
11050       seq->rowgraph_mtd->userdata = (void *) seq ;
11051    }
11052 
11053    EXRETURN ;
11054 }
11055 
11056 /*-----------------------------------------------------------------------*/
11057 /*! This function is called when then rowgraph_mtd is killed.            */
11058 
ISQ_rowgraph_mtdkill(MEM_topshell_data * mp)11059 void ISQ_rowgraph_mtdkill( MEM_topshell_data *mp )
11060 {
11061    MCW_imseq *seq ;
11062 
11063 ENTRY("ISQ_rowgraph_mtdkill") ;
11064 
11065    if( mp == NULL ) EXRETURN ;
11066    seq = (MCW_imseq *) mp->userdata ; if( ! ISQ_VALID(seq) ) EXRETURN ;
11067 
11068    seq->rowgraph_mtd = NULL ;
11069 
11070    AV_assign_ival( seq->rowgraph_av , 0 ) ;
11071    seq->rowgraph_num = 0 ;
11072    EXRETURN ;
11073 }
11074 
11075 /*-----------------------------------------------------------------------*/
11076 /*! This function is called when the graymap_mtd is killed.              */
11077 
ISQ_graymap_mtdkill(MEM_topshell_data * mp)11078 void ISQ_graymap_mtdkill( MEM_topshell_data *mp )  /* 24 Oct 2003 */
11079 {
11080    MCW_imseq *seq ;
11081 
11082 ENTRY("ISQ_graymap_mtdkill") ;
11083 
11084    if( mp == NULL ) EXRETURN ;
11085    seq = (MCW_imseq *) mp->userdata ;
11086    if( ISQ_VALID(seq) ){
11087      seq->graymap_mtd = NULL ;
11088      seq->need_orim &= ~GRAYMAP_MASK ;  /* turn off need for orim for graymap */
11089    }
11090 
11091    EXRETURN ;
11092 }
11093 
11094 /*-----------------------------------------------------------------------*/
11095 
ISQ_graymap_draw(MCW_imseq * seq)11096 void ISQ_graymap_draw( MCW_imseq *seq )  /* 24 Oct 2003 */
11097 {
11098    MEM_plotdata *mp ;
11099    int ix , nx , ny , nxx ;
11100    float *yar[2] , *xar , dx , *ar ;
11101 
11102 ENTRY("ISQ_graymap_draw") ;
11103 
11104    if( !ISQ_REALZ(seq) || seq->dc->use_xcol_im ) EXRETURN ;  /* error */
11105 
11106    seq->need_orim |= GRAYMAP_MASK ;
11107 
11108    /* make float arrays with grayscales and data range */
11109    /* Modifed 12 Jan 2004 to plot in histogram style. */
11110 
11111    nx     = seq->dc->ncol_im ;
11112    nxx    = 2*nx+2 ;
11113    ny     = 1 ;
11114    dx     = (seq->bartop - seq->barbot) / nx ; if( dx == 0.0 ) EXRETURN ;
11115    yar[0] = (float *) malloc( sizeof(float)*nxx ) ;
11116    xar    = (float *) malloc( sizeof(float)*nxx ) ;
11117    xar[0] = seq->barbot ;
11118    for( ix=0 ; ix < nx ; ix++ ){
11119      xar[2*ix+1]     = seq->barbot + ix*dx ;
11120      xar[2*ix+2]     = seq->barbot + (ix+1)*dx ;
11121      yar[0][2*ix+1]  = seq->dc->xint_im[ix] ;
11122      if( yar[0][2*ix+1] < 0.0 ){
11123        yar[0][2*ix+1] = 0.0 ;
11124      } else {
11125        yar[0][2*ix+1] *= (255.0/65280.0);
11126        if( yar[0][2*ix+1] > 255.0 ) yar[0][2*ix+1] = 255.0;
11127      }
11128      yar[0][2*ix+2] = yar[0][2*ix+1] ;
11129    }
11130    xar[2*nx+1]    = seq->bartop ;
11131    yar[0][0]      = yar[0][1] ;
11132    yar[0][2*nx+1] = yar[0][2*nx] ;
11133 
11134    /* histogram the image? */
11135 
11136    if( seq->orim != NULL ){
11137      float *iar=MRI_FLOAT_PTR(seq->orim) , *har , val ;
11138      float scl=nx/(seq->bartop-seq->barbot) ; int ii,jj ;
11139      har = (float *) calloc( sizeof(float),nx  ) ;
11140      for( ii=0 ; ii < seq->orim->nvox ; ii++ ){
11141        jj = (int)( scl*(iar[ii]-seq->barbot) ) ;
11142        if( jj < 0 ) jj = 0 ; else if( jj > nx-1 ) jj = nx-1 ;
11143        har[jj] += 1.0 ;
11144      }
11145      for( scl=0.0,ii=1 ; ii < nx ; ii++ )
11146        if( har[ii] > scl ) scl = har[ii] ;
11147      if( scl > 0.0 ){
11148        ny = 2 ;
11149        yar[1] = (float *) malloc( sizeof(float)*nxx ) ;
11150        scl = 255.0/sqrt(scl) ;
11151        yar[1][0] = yar[1][2*nx+1] = 0.0 ;
11152        for( ii=0 ; ii < nx ; ii++ ){
11153          val = scl*sqrt(har[ii]) ; if( val > 255.0 ) val = 255.0 ;
11154          yar[1][2*ii+1] = yar[1][2*ii+2] = val ;
11155        }
11156      }
11157      free( (void *)har ) ;
11158    }
11159 
11160    /* make a plot in memory */
11161 
11162    plot_ts_setTHIK(0.002468f) ;
11163    mp = plot_ts_mem( nxx,xar, ny,0,yar, "Data Value",
11164                      (ny == 1) ? "GrayLevel"
11165                                : "GrayLevel\\red/Histogram\\black" ,
11166                      NULL,NULL ) ;
11167    free(xar); free(yar[0]); if( ny == 2 ) free(yar[1]) ;
11168    if( mp == NULL ){
11169      fprintf(stderr,"*** error in ISQ_graymap_draw: can't make plot_ts_mem\n") ;
11170      EXRETURN ;  /* error */
11171    }
11172 
11173    /* if there is a plot window open, plot into it, otherwise open a new window */
11174 
11175    if( seq->graymap_mtd != NULL ){
11176 
11177       MTD_replace_plotdata( seq->graymap_mtd , mp ) ;
11178       redraw_topshell( seq->graymap_mtd ) ;
11179 
11180    } else {  /* make a new plot window */
11181 
11182       seq->graymap_mtd = memplot_to_topshell( seq->dc->display, mp, ISQ_graymap_mtdkill ) ;
11183       if( seq->graymap_mtd == NULL ){ delete_memplot(mp); EXRETURN; }
11184       seq->graymap_mtd->userdata = (void *) seq ;
11185       ISQ_place_widget( seq->wtop , seq->graymap_mtd->top ) ;
11186    }
11187 
11188    EXRETURN ;
11189 }
11190 
11191 /*-----------------------------------------------------------------------
11192    21 Jan 1999: Handle the surface graph stuff
11193 -------------------------------------------------------------------------*/
11194 
ISQ_surfgraph_label(MCW_arrowval * av,XtPointer cd)11195 char * ISQ_surfgraph_label( MCW_arrowval *av , XtPointer cd )
11196 {
11197    switch( av->ival ){
11198       case 0:  return "No"  ;
11199       case 1:  return "Yes" ;
11200       case 2:  return "Inv" ;
11201    }
11202    return "?*?" ;
11203 }
11204 
11205 /*-------- called when the user changes the SurfGraph menu button --------*/
11206 
ISQ_surfgraph_CB(MCW_arrowval * av,XtPointer cd)11207 void ISQ_surfgraph_CB( MCW_arrowval *av , XtPointer cd )
11208 {
11209    MCW_imseq *seq = (MCW_imseq *) cd ;
11210 
11211 ENTRY("ISQ_surfgraph_CB") ;
11212 
11213    if( ! ISQ_VALID(seq) ) EXRETURN ;                /* bad input */
11214    if( av->ival == seq->surfgraph_num ) EXRETURN ;  /* nothing changed */
11215 
11216    seq->surfgraph_num = av->ival ;
11217 
11218    if( seq->surfgraph_num > 0 ) seq->need_orim |=  SURFGRAPH_MASK ;
11219    else                         seq->need_orim &= ~SURFGRAPH_MASK ;
11220    if( seq->need_orim == 0 ) KILL_1MRI(seq->orim) ;
11221 
11222    ISQ_redisplay( seq , -1 , isqDR_reimage ) ;  /* redo current image */
11223    EXRETURN ;
11224 }
11225 
11226 /*---------------- called to redraw the surface graph -------------------*/
11227 
ISQ_surfgraph_draw(MCW_imseq * seq)11228 void ISQ_surfgraph_draw( MCW_imseq *seq )
11229 {
11230    MEM_plotdata *mp ;
11231    ISQ_cbs cbs ;
11232    int ix , jy ;
11233 
11234 ENTRY("ISQ_surfgraph_draw") ;
11235 
11236    if( ! ISQ_REALZ(seq) ) EXRETURN ;  /* error */
11237 
11238    /* marked for no graph? */
11239 
11240    if( seq->surfgraph_num == 0 ){
11241      if( seq->surfgraph_mtd != NULL ){
11242        plotkill_topshell( seq->surfgraph_mtd ) ;
11243        seq->surfgraph_mtd = NULL ;
11244      }
11245      EXRETURN ;
11246    }
11247 
11248    if( seq->orim == NULL ) EXRETURN ;
11249 
11250    /* find current location */
11251 
11252    if( ISQ_SKIP_OVERLAY(seq) ){
11253       ix = jy = -1 ;
11254    } else {
11255       cbs.reason = isqCR_getxynim ;
11256       cbs.xim = cbs.yim = cbs.nim = -666 ;
11257       if( seq->status->send_CB != NULL )
11258 #if 0
11259          seq->status->send_CB( seq , seq->getaux , &cbs ) ;
11260 #else
11261          SEND(seq,cbs) ;
11262 #endif
11263       if( cbs.xim < 0 || cbs.yim < 0 ){
11264          ix = jy = -1 ;
11265       } else {
11266          ISQ_unflipxy( seq , &(cbs.xim) , &(cbs.yim) ) ;
11267          ix = cbs.xim ; jy = cbs.yim ;
11268       }
11269    }
11270 
11271    /* plot the data */
11272 
11273    mp = plot_image_surface( seq->orim , (seq->surfgraph_num == 2) ? -1.0 : 1.0 ,
11274                             seq->surfgraph_theta , seq->surfgraph_phi ,
11275                             ix , jy ) ;
11276    if( mp == NULL ) EXRETURN ;
11277 
11278    /* if there is a plot window open, plot into it, otherwise open a new window */
11279 
11280    if( seq->surfgraph_mtd != NULL ){
11281 
11282       MTD_replace_plotdata( seq->surfgraph_mtd , mp ) ;
11283       redraw_topshell( seq->surfgraph_mtd ) ;
11284 
11285    } else {  /* make a new plot window */
11286 
11287       seq->surfgraph_mtd = memplot_to_topshell( seq->dc->display, mp, ISQ_surfgraph_mtdkill ) ;
11288 
11289       if( seq->surfgraph_mtd == NULL ){ delete_memplot(mp); EXRETURN; }
11290 
11291       seq->surfgraph_mtd->userdata = (void *)seq ;
11292 
11293       /* add an arrowpad to it (lower right corner) */
11294 
11295       WAIT_for_window(seq->surfgraph_mtd->top) ;
11296 
11297       seq->surfgraph_arrowpad = new_MCW_arrowpad( seq->surfgraph_mtd->form ,
11298                                                   ISQ_surfgraph_arrowpad_CB ,
11299                                                   (XtPointer)seq ) ;
11300 
11301       /* XtUnmanageChild( seq->surfgraph_arrowpad->wform ) ; */
11302 
11303       XtVaSetValues( seq->surfgraph_arrowpad->wform ,
11304                         XmNbottomAttachment , XmATTACH_FORM ,
11305                         XmNrightAttachment  , XmATTACH_FORM ,
11306                         XmNleftAttachment   , XmATTACH_NONE ,
11307                         XmNtopAttachment    , XmATTACH_NONE ,
11308                         XmNwidth            , 60 ,
11309                         XmNheight           , 60 ,
11310                      NULL ) ;
11311 
11312       MCW_set_widget_bg( seq->surfgraph_arrowpad->wform , "white" , 0 ) ;
11313 
11314       XtManageChild( seq->surfgraph_arrowpad->wform ) ;
11315 
11316       seq->surfgraph_arrowpad->parent = (XtPointer)seq ;
11317       seq->surfgraph_arrowpad->fastdelay = MCW_AV_longdelay ;
11318    }
11319 
11320    EXRETURN ;
11321 }
11322 
11323 /*-----------------------------------------------------------*/
11324 /*--- Called when the user kills the surface graph window ---*/
11325 
ISQ_surfgraph_mtdkill(MEM_topshell_data * mp)11326 void ISQ_surfgraph_mtdkill( MEM_topshell_data *mp )
11327 {
11328    MCW_imseq *seq ;
11329 
11330 ENTRY("ISQ_surfgraph_mtdkill") ;
11331 
11332    if( mp == NULL ) EXRETURN ;
11333    seq = (MCW_imseq *) mp->userdata ; if( ! ISQ_VALID(seq) ) EXRETURN ;
11334 
11335    seq->surfgraph_mtd   = NULL ;
11336    seq->surfgraph_theta = DEFAULT_THETA  ;
11337    seq->surfgraph_phi   = DEFAULT_PHI ;
11338    myXtFree( seq->surfgraph_arrowpad ) ;
11339 
11340    seq->surfgraph_num = 0 ;
11341    AV_assign_ival( seq->surfgraph_av , 0 ) ;
11342    EXRETURN ;
11343 }
11344 
11345 /*--- actually draws an image to a wiremesh, in memory ---*/
11346 
plot_image_surface(MRI_IMAGE * im,float fac,float theta,float phi,int ix,int jy)11347 MEM_plotdata * plot_image_surface( MRI_IMAGE *im , float fac ,
11348                                    float theta , float phi , int ix , int jy )
11349 {
11350    MRI_IMAGE *fim , *qim ;
11351    MEM_plotdata *mp ;
11352    float *x , *y , *z ;
11353    float  dx ,  dy , zbot,ztop ;
11354    int ii , nx , ny , nxy ;
11355    char str[128] ;
11356 
11357 ENTRY("plot_image_surface") ;
11358 
11359    if( im == NULL ) RETURN( NULL );
11360 
11361    /*-- setup to plot --*/
11362 
11363    nx = im->nx ; ny = im->ny ;
11364    if( nx < 3 || ny < 3 ) RETURN( NULL );
11365 
11366    create_memplot_surely( "imsurf" , 1.1 ) ;
11367 
11368    dx = im->dx ; if( dx <= 0.0 ) dx = 1.0 ;
11369    dy = im->dy ; if( dy <= 0.0 ) dy = 1.0 ;
11370 
11371    x = (float *) malloc( sizeof(float) * nx ) ;
11372    for( ii=0 ; ii < nx ; ii++ ) x[ii] = ii * dx ;
11373 
11374    y = (float *) malloc( sizeof(float) * ny ) ;
11375    for( ii=0 ; ii < ny ; ii++ ) y[ii] = ii * dy ;
11376 
11377    (void)thd_floatscan( nx , x ) ;
11378    (void)thd_floatscan( ny , y ) ;
11379 
11380    /*-- scale image data --*/
11381 
11382    qim = mri_flippo( MRI_ROT_180 , 1 , im ) ;
11383    if( fac == 1.0 || fac == 0.0 ) fim = mri_to_float(qim) ;
11384    else                           fim = mri_scale_to_float(fac,qim) ;
11385    z = MRI_FLOAT_PTR(fim) ; mri_free(qim) ;
11386    nxy = nx * ny ; zbot = ztop = z[0] ;
11387    for( ii=1 ; ii < nxy ; ii++ ){
11388            if( z[ii] < zbot ) zbot = z[ii] ;
11389       else if( z[ii] > ztop ) ztop = z[ii] ;
11390    }
11391    ztop = ztop - zbot ;
11392    if( ztop > 0.0 ){
11393       ztop = 0.85 * sqrt( x[nx-1] * y[ny-1] ) / ztop ;
11394       for( ii=0 ; ii < nxy ; ii++ ) z[ii] = (z[ii]-zbot) * ztop ;
11395    }
11396 
11397    /*-- plot surface --*/
11398 
11399    set_color_memplot( 0.0 , 0.0 , 0.0 ) ;
11400    set_thick_memplot( 0.0 ) ;
11401    plotpak_srface( x , y , z , nx , ny , theta, phi ) ;
11402 
11403    /*-- plot a * at the selected point (if it is in range) --*/
11404 
11405    if( ix >= 0 && ix < nx && jy >= 0 && jy < ny ){
11406       real xi,yi,zi ; float xt,yt,zt , xtp,ytp,ztp ;
11407       ii = 1 ;
11408       xi = x[ix] ; yi = y[ny-1-jy] ; zi = z[ix+(ny-1-jy)*nx] ;
11409       (void) trn32s_( &xi , &yi , &zi ,
11410                       (real *)(&xt) , (real *)(&yt) , (real *)(&zt) ,
11411                       (integer *)(&ii) ) ;
11412 
11413 #undef  THIK
11414 #define THIK 0.00333
11415 
11416       dx = 0.016 * x[nx-1] ; dy = 0.016 * y[ny-1] ; dx = MAX(dx,dy) ;
11417       xi = x[ix]+dx ; yi = y[ny-1-jy]+dx ; zi = z[ix+(ny-1-jy)*nx] ;
11418       (void) trn32s_( &xi , &yi , &zi ,
11419                       (real *)(&xtp) , (real *)(&ytp) , (real *)(&ztp) ,
11420                       (integer *)(&ii) ) ;
11421       dx = fabs(xtp-xt) ; dy = fabs(ytp-yt) ; dx = MAX(dx,dy) ;
11422 
11423       set_color_memplot( 0.8 , 0.0 , 0.2 ) ;
11424       set_thick_memplot( THIK ) ;
11425       plotpak_line( xt-dx , yt    , xt+dx , yt    ) ; /* - stroke */
11426       plotpak_line( xt    , yt-dx , xt    , yt+dx ) ; /* | stroke */
11427       plotpak_line( xt-dx , yt-dx , xt+dx , yt+dx ) ; /* / stroke */
11428       plotpak_line( xt+dx , yt-dx , xt-dx , yt+dx ) ; /* \ stroke */
11429       set_color_memplot( 0.2 , 0.0 , 0.8 ) ;
11430       plotpak_line( xt+dx , yt-dx , xt+dx , yt+dx ) ; /* box around outside */
11431       plotpak_line( xt+dx , yt+dx , xt-dx , yt+dx ) ;
11432       plotpak_line( xt-dx , yt+dx , xt-dx , yt-dx ) ;
11433       plotpak_line( xt-dx , yt-dx , xt+dx , yt-dx ) ;
11434       set_color_memplot( 0.0 , 0.0 , 0.0 ) ;
11435       set_thick_memplot( 0.0 ) ;
11436    }
11437 
11438    free(x); free(y) ; mri_free(fim);
11439 
11440    plotpak_set( 0.0,1.0 , 0.0,1.0 , 0.0,1.0 , 0.0,1.0 , 1 ) ;
11441    sprintf(str,"\\theta=%.0f\\degree \\phi=%.0f\\degree",theta,phi) ;
11442    plotpak_pwritf( 1.099 , 0.97 , str, 19 , 0 , 1 ) ;
11443 
11444    mp = get_active_memplot() ; RETURN( mp );
11445 }
11446 
11447 /*--- called when the user presses a surface graph arrowpad button ---*/
11448 
ISQ_surfgraph_arrowpad_CB(MCW_arrowpad * apad,XtPointer client_data)11449 void ISQ_surfgraph_arrowpad_CB( MCW_arrowpad *apad , XtPointer client_data )
11450 {
11451    MCW_imseq *seq = (MCW_imseq *) client_data ;
11452    XButtonEvent *xev = (XButtonEvent *) &(apad->xev) ;
11453    float step = 10.0 ;
11454 
11455 ENTRY("ISQ_surfgraph_arrowpad_CB") ;
11456 
11457    if( ! ISQ_REALZ(seq) ) EXRETURN ;  /* error */
11458 
11459    if( ( xev->type == ButtonPress || xev->type == ButtonRelease ) ){
11460       if( xev->state & (ShiftMask|ControlMask) ) step = 90.0 ; /* big step   */
11461       if( xev->state & Mod1Mask                ) step =  2.0 ; /* small step */
11462    }
11463 
11464    switch( apad->which_pressed ){
11465       case AP_MID:   seq->surfgraph_theta = DEFAULT_THETA ;
11466                      seq->surfgraph_phi   = DEFAULT_PHI   ; break ;
11467 
11468       case AP_DOWN:  seq->surfgraph_theta += step ; break ;
11469       case AP_UP:    seq->surfgraph_theta -= step ; break ;
11470       case AP_LEFT:  seq->surfgraph_phi   += step ; break ;
11471       case AP_RIGHT: seq->surfgraph_phi   -= step ; break ;
11472 
11473       default:                                   EXRETURN ; /* error */
11474    }
11475 
11476    while( seq->surfgraph_theta < 0.0    ) seq->surfgraph_theta += 360.0 ;
11477    while( seq->surfgraph_theta >= 360.0 ) seq->surfgraph_theta -= 360.0 ;
11478 
11479    while( seq->surfgraph_phi < 0.0    ) seq->surfgraph_phi += 360.0 ;
11480    while( seq->surfgraph_phi >= 360.0 ) seq->surfgraph_phi -= 360.0 ;
11481 
11482    ISQ_surfgraph_draw( seq ) ; EXRETURN ;
11483 }
11484 
11485 /*-----------------------------------------------------------------------
11486   24 Apr 2001: remove a widget from the onoff list,
11487                and permanently unmanage it (for the recorder)
11488 -------------------------------------------------------------------------*/
11489 
ISQ_remove_widget(MCW_imseq * seq,Widget w)11490 void ISQ_remove_widget( MCW_imseq *seq , Widget w )
11491 {
11492    int ii ;
11493 ENTRY("ISQ_remove_onoff") ;
11494 
11495    if( !ISQ_VALID(seq) || w == NULL ) EXRETURN ;
11496 
11497    XtUnmanageChild( w ) ;  /* turn it off */
11498 
11499    for( ii=0 ; ii < seq->onoff_num ; ii++ ){     /* find in list */
11500      if( w == seq->onoff_widgets[ii] ){
11501        seq->onoff_widgets[ii] = NULL ;
11502        break ;
11503      }
11504    }
11505 
11506    for( ii=seq->onoff_num-1 ; ii > 0 ; ii-- ){   /* truncate list */
11507      if( seq->onoff_widgets[ii] == NULL )
11508        seq->onoff_num = ii ;
11509      else
11510        break ;
11511    }
11512 
11513    EXRETURN ;
11514 }
11515 
11516 /*-----------------------------------------------------------------------
11517   24 Apr 2001: recording button and accoutrements
11518 -------------------------------------------------------------------------*/
11519 
ISQ_record_button(MCW_imseq * seq)11520 void ISQ_record_button( MCW_imseq *seq )
11521 {
11522    Widget rc , mbar , menu , cbut , wpar ;
11523    XmString xstr ;
11524 
11525 ENTRY("ISQ_record_button") ;
11526 
11527    /*--- make the widgets ---*/
11528 
11529    /* rowcol to hold the menubar */
11530 
11531    seq->onoff_widgets[(seq->onoff_num)++] = seq->record_rc = rc =
11532      XtVaCreateWidget(
11533            "imseq" , xmRowColumnWidgetClass , seq->wform ,
11534               XmNorientation    , XmHORIZONTAL ,
11535               XmNpacking        , XmPACK_TIGHT ,
11536 
11537               LEADING_BOT       , XmATTACH_WIDGET              ,
11538               LEADING_WIDGET_BOT, seq->wbut_bot[NBUTTON_BOT-1] ,
11539               EDGING_BOT        , XmATTACH_FORM                ,
11540 
11541               XmNmarginWidth  , 1 ,
11542               XmNmarginHeight , 0 ,
11543               XmNmarginBottom , 0 ,
11544               XmNmarginTop    , 0 ,
11545               XmNmarginLeft   , 0 ,
11546               XmNmarginRight  , 0 ,
11547               XmNspacing      , 0 ,
11548               XmNborderWidth  , 0 ,
11549               XmNborderColor  , 0 ,
11550 
11551               XmNrecomputeSize , False ,
11552               XmNtraversalOn , False ,
11553               XmNinitialResourcesPersistent , False ,
11554            NULL ) ;
11555 
11556    /* menubar to hold the cascade button */
11557 
11558    mbar = XmCreateMenuBar( rc , "imseq" , NULL,0 ) ;
11559    XtVaSetValues( mbar ,
11560                      XmNmarginWidth  , 1 ,
11561                      XmNmarginHeight , 0 ,
11562                      XmNmarginBottom , 0 ,
11563                      XmNmarginTop    , 0 ,
11564                      XmNmarginLeft   , 0 ,
11565                      XmNmarginRight  , 0 ,
11566                      XmNspacing      , 0 ,
11567                      XmNborderWidth  , 0 ,
11568                      XmNborderColor  , 0 ,
11569                      XmNtraversalOn  , False ,
11570                      XmNbackground   , seq->dc->ovc->pixov_brightest ,
11571                   NULL ) ;
11572 
11573    /* the menu pane */
11574 
11575    menu = XmCreatePulldownMenu( mbar , "menu" , NULL,0 ) ;
11576    VISIBILIZE_WHEN_MAPPED(menu) ;
11577 #if 0  /* doesn't work well */
11578    if( !AFNI_yesenv("AFNI_DISABLE_TEAROFF") ) TEAROFFIZE(menu) ;
11579 #endif
11580 
11581    /* the cascade button (what the user sees) */
11582 
11583    xstr = XmStringCreateLtoR( "Rec" , XmFONTLIST_DEFAULT_TAG ) ;
11584    seq->record_cbut = cbut =
11585      XtVaCreateManagedWidget(
11586             "imseq" , xmCascadeButtonWidgetClass , mbar ,
11587                XmNlabelString , xstr ,
11588                XmNsubMenuId   , menu ,
11589                XmNmarginWidth , 1 ,
11590                XmNmarginHeight, 0 ,
11591                XmNmarginBottom, 0 ,
11592                XmNmarginTop   , 0 ,
11593                XmNmarginRight , 0 ,
11594                XmNmarginLeft  , 0 ,
11595                XmNtraversalOn , False ,
11596                XmNinitialResourcesPersistent , False ,
11597             NULL ) ;
11598    XmStringFree( xstr ) ;
11599    XtManageChild( mbar ) ;
11600    MCW_register_hint( cbut , "Turn image recording on/off" ) ;
11601    MCW_register_help( cbut ,
11602                       " \n"
11603                       "This menu controls image recording. Whenever the image\n"
11604                       "displayed is altered, an RGB copy of it can be saved\n"
11605                       "into a separate image buffer.  In this way, you can\n"
11606                       "build a sequence of images that can later be written\n"
11607                       "to disk for further processing (e.g., animation).\n"
11608                       "\n"
11609                       "---- These options control WHEN images  ----\n"
11610                       "---- will be recorded into the sequence ----\n"
11611                       "\n"
11612                       " Off      = don't record\n"
11613                       " Next One = record next image, then turn Off\n"
11614                       " Stay On  = record all images\n"
11615                       "\n"
11616                       "---- These options control WHERE new images ----\n"
11617                       "---- are to be stored into the sequence     ----\n"
11618                       "\n"
11619                       " After End    = at tail of sequence\n"
11620                       " Before Start = at head of sequence\n"
11621                       " Insert --    = insert before current sequence position\n"
11622                       " Insert ++    = insert after current sequence position\n"
11623                       " OverWrite    = replace current sequence position\n"
11624                       " -- OverWrite = replace image before current position\n"
11625                       " ++ OverWrite = replace image after current position\n"
11626                       "\n"
11627                       "---- HINTS and NOTES ----\n"
11628                       "\n"
11629                       "* You may want to set Xhairs to 'Off' on the AFNI\n"
11630                       "   control panel before recording images.\n"
11631                       "* The recording window is like a dataset image\n"
11632                       "   viewing window with most controls removed.\n"
11633                       "   The slider moves between recorded images, rather\n"
11634                       "   than between slices.\n"
11635                       "* The new 'Kill' button in the recording window lets\n"
11636                       "   you erase one image from the recorded sequence.\n"
11637                       "   Erased images, if not overwritten, will NOT be\n"
11638                       "   saved to disk.\n"
11639                       "* Use 'Save:bkg' in the recording window to save the\n"
11640                       "   sequence of recorded images to disk in PPM format.\n"
11641                       "   The recorded images are in color, and will be saved\n"
11642                       "   in color (despite the :bkg label on the Save button).\n"
11643                       "* You may want to use set 'Warp Anat on Demand' on\n"
11644                       "   the Datamode control panel to force the display\n"
11645                       "   voxels to be cubical.  Otherwise, the saved image\n"
11646                       "   pixels will have the same aspect ratio as the voxels\n"
11647                       "   in the dataset, which may not be square!\n"
11648                      ) ;
11649 
11650    /*-- top of menu = a label to click on that does nothing at all --*/
11651 
11652    /* This --- Cancel --- label does not cause the hangup, so it is
11653    left alone. See related comments in afni_graph.c LessTif patrol, Jan 07 09 */
11654 
11655    xstr = XmStringCreateLtoR( "-- Cancel --" , XmFONTLIST_DEFAULT_TAG ) ;
11656    wwtem = XtVaCreateManagedWidget(
11657             "menu" , xmLabelWidgetClass , menu ,
11658                XmNlabelString , xstr ,
11659                XmNrecomputeSize , False ,
11660                XmNinitialResourcesPersistent , False ,
11661             NULL ) ;
11662    XmStringFree(xstr) ; LABELIZE(wwtem) ;
11663 
11664    (void) XtVaCreateManagedWidget(
11665             "menu" , xmSeparatorWidgetClass , menu ,
11666                XmNseparatorType , XmSINGLE_LINE ,
11667             NULL ) ;
11668 
11669    /*-- menu toggles switches --*/
11670 
11671    {  static char *status_label[3] = { "Off" , "Next One" , "Stay On" } ;
11672       static char *method_label[7] = { "After End"    ,
11673                                        "Before Start" ,
11674                                        "Insert --"    ,
11675                                        "Insert ++"    ,
11676                                        "OverWrite"    ,
11677                                        "-- OverWrite" ,
11678                                        "++ OverWrite"   } ;
11679 
11680       seq->record_status_bbox =
11681          new_MCW_bbox( menu , 3,status_label ,
11682                        MCW_BB_radio_one , MCW_BB_noframe ,
11683                        ISQ_record_CB , (XtPointer) seq ) ;
11684       seq->record_status = RECORD_STATUS_OFF ;
11685 
11686       (void) XtVaCreateManagedWidget(
11687                "menu" , xmSeparatorWidgetClass , menu ,
11688                   XmNseparatorType , XmSINGLE_LINE ,
11689                NULL ) ;
11690 
11691       seq->record_method_bbox =
11692          new_MCW_bbox( menu , 7,method_label ,
11693                        MCW_BB_radio_one , MCW_BB_noframe ,
11694                        ISQ_record_CB , (XtPointer) seq ) ;
11695       seq->record_method = RECORD_METHOD_AFTEREND ;
11696    }
11697 
11698    /*-- done with Widgets --*/
11699 
11700    XtManageChild( rc ) ;
11701 
11702    /*-- setup other variables --*/
11703 
11704    seq->record_mode  = 0 ;    /* not a recorder itself (yet) */
11705    seq->record_imseq = NULL ; /* doesn't have a recorder */
11706    seq->record_imarr = NULL ; /* doesn't have a recorded sequence */
11707    seq->record_mplot = NULL ; /* 05 Jan 2005 */
11708 
11709    EXRETURN ;
11710 }
11711 
11712 /*-----------------------------------------------------------------------
11713   Callback for toggle actions in the record menu
11714 -------------------------------------------------------------------------*/
11715 
ISQ_record_CB(Widget w,XtPointer client_data,XtPointer call_data)11716 void ISQ_record_CB( Widget w, XtPointer client_data, XtPointer call_data )
11717 {
11718    MCW_imseq *seq = (MCW_imseq *) client_data ;
11719    int ib ;
11720 
11721 ENTRY("ISQ_record_CB") ;
11722 
11723    if( !ISQ_REALZ(seq) ) EXRETURN ;
11724 
11725    ib = MCW_val_bbox( seq->record_status_bbox ) ;
11726    if( ib != seq->record_status ){
11727       if( RECORD_ISON(ib) != RECORD_ISON(seq->record_status) )
11728          MCW_invert_widget( seq->record_cbut ) ;
11729       seq->record_status = ib ;
11730    }
11731 
11732    ib = MCW_val_bbox( seq->record_method_bbox ) ;
11733    if( ib != seq->record_method ){
11734       seq->record_method = ib ;
11735    }
11736 
11737    EXRETURN ;
11738 }
11739 
11740 /*----------------------------------------------------------------------
11741   Insert the current image from seq into seq's recorder, at index pos,
11742   with method meth: -1 => insert before pos
11743                     +1 => insert after pos
11744                      0 => overwrite pos
11745   If pos < 0, then it means
11746                     -1 => current position
11747                     -2 => before current position
11748                     -3 => after current position
11749   If pos >= 0, then it is an index into the recorded image sequence.
11750   If it is past the end of the sequence, then it means the end.
11751   The recorder is left positioned at the new image.
11752 ------------------------------------------------------------------------*/
11753 
ISQ_record_addim(MCW_imseq * seq,int pos,int meth)11754 void ISQ_record_addim( MCW_imseq *seq , int pos , int meth )
11755 {
11756    MRI_IMAGE *tim ;
11757    int opos , ii,bot,top ;
11758 
11759 ENTRY("ISQ_record_addim") ;
11760 
11761    /* sanity checks */
11762 
11763    if( !ISQ_REALZ(seq)        ||
11764        seq->record_mode       ||
11765        seq->given_xim == NULL   ) EXRETURN; /* bad */
11766 
11767    /* if recorded image sequence doesn't exist, create it */
11768 
11769    if( seq->record_imarr == NULL ){
11770      INIT_IMARR(seq->record_imarr) ;
11771      meth = 1 ;  /* change meth for this special case */
11772 
11773      seq->record_mplot = NULL ;  /* 05 Jan 2005 */
11774    }
11775 
11776    /* convert current XImage to RGB format */
11777 
11778    tim = XImage_to_mri( seq->dc, seq->given_xim, X2M_USE_CMAP|X2M_FORCE_RGB );
11779 
11780    if( tim == NULL ) EXRETURN ; /* bad */
11781 
11782    /* figure out where to put this image in the list */
11783 
11784    opos = pos ;
11785    if( opos < 0 ){  /* need current position of recorder */
11786 
11787       if( seq->record_imseq != NULL ){
11788          drive_MCW_imseq( seq->record_imseq, isqDR_getimnr, (XtPointer)&opos );
11789               if( pos == -2 && opos > 0                                ) opos--;
11790          else if( pos == -3 && opos < IMARR_COUNT(seq->record_imarr)-1 ) opos++;
11791       }
11792       else
11793          opos = -1 ; /* special case */
11794 
11795    } else if( opos >= IMARR_COUNT(seq->record_imarr)-1 ) {
11796 
11797       opos = IMARR_COUNT(seq->record_imarr)-1 ;
11798    }
11799 
11800    if( opos < 0 ) meth = 1 ; /* special case: sequence is empty now */
11801 
11802    /* if we are inserting, we need to add an image */
11803 
11804    if( meth != 0 ){
11805 
11806       ADDTO_IMARR( seq->record_imarr , NULL ) ;  /* add at end */
11807       seq->record_mplot =(MEM_plotdata **)       /* 05 Jan 2005 */
11808                          realloc( (void *)seq->record_mplot ,
11809                                   sizeof(MEM_plotdata *)
11810                                  *IMARR_COUNT(seq->record_imarr) ) ;
11811       bot = (meth < 0) ? opos : opos+1 ;         /* move images up */
11812       top = IMARR_COUNT(seq->record_imarr)-2 ;
11813       for( ii=top ; ii >= bot ; ii-- ){
11814         IMARR_SUBIM(seq->record_imarr,ii+1) = IMARR_SUBIM(seq->record_imarr,ii);
11815         seq->record_mplot[ii+1] = seq->record_mplot[ii] ;  /* 05 Jan 2005 */
11816       }
11817 
11818       IMARR_SUBIM(seq->record_imarr,bot) = tim ; /* insert */
11819       seq->record_mplot[bot]             = copy_memplot( seq->mplot ) ;
11820 
11821    } else {  /* overwrite image */
11822 
11823       bot = opos ;
11824       mri_free( IMARR_SUBIM(seq->record_imarr,bot) ) ; /* out with the old */
11825       IMARR_SUBIM(seq->record_imarr,bot) = tim ;       /* in with the new */
11826 
11827       delete_memplot( seq->record_mplot[bot] ) ;       /* 05 Jan 2005 */
11828       seq->record_mplot[bot] = copy_memplot( seq->mplot ) ;
11829    }
11830 
11831    /* at this point, we have put the new image into location bot in the array */
11832 
11833    /* if the recorder isn't open now, open it, otherwise update it */
11834 
11835    if( seq->record_imseq == NULL )
11836       ISQ_record_open( seq ) ;
11837    else
11838       ISQ_record_update( seq , bot ) ;
11839 
11840    EXRETURN ;
11841 }
11842 
11843 /*-----------------------------------------------------------------------*/
11844 
ISQ_record_open(MCW_imseq * seq)11845 void ISQ_record_open( MCW_imseq *seq )
11846 {
11847    int ntot ;
11848 
11849 ENTRY("ISQ_record_open") ;
11850 
11851    if( !ISQ_REALZ(seq)                     ||
11852        seq->record_imarr == NULL           ||
11853        IMARR_COUNT(seq->record_imarr) == 0   ) EXRETURN ;
11854 
11855    ntot = IMARR_COUNT(seq->record_imarr) ;
11856 
11857    seq->record_imseq = open_MCW_imseq( seq->dc , ISQ_record_getim , seq ) ;
11858    seq->record_imseq->parent = seq ;
11859 
11860    drive_MCW_imseq( seq->record_imseq , isqDR_record_mode , NULL ) ;
11861 
11862    drive_MCW_imseq( seq->record_imseq , isqDR_realize, NULL ) ;
11863 
11864 #ifndef DONT_ONOFF_ONE
11865    if( ntot == 1 )
11866       drive_MCW_imseq( seq->record_imseq,isqDR_onoffwid,(XtPointer)isqDR_offwid);
11867    else
11868       drive_MCW_imseq( seq->record_imseq,isqDR_onoffwid,(XtPointer)isqDR_onwid );
11869 #endif
11870 
11871    drive_MCW_imseq( seq->record_imseq , isqDR_reimage , (XtPointer)ITOP(ntot-1) ) ;
11872 
11873    ISQ_set_cursor_state( seq , -1 ) ;  /* 10 Mar 2003 */
11874    NORMAL_cursorize( seq->wbar ) ;
11875 
11876    EXRETURN ;
11877 }
11878 
11879 /*-----------------------------------------------------------------------*/
11880 
ISQ_record_update(MCW_imseq * seq,int npos)11881 void ISQ_record_update( MCW_imseq *seq , int npos )
11882 {
11883    int ntot , ii ;
11884 
11885 ENTRY("ISQ_record_update") ;
11886 
11887    if( !ISQ_REALZ(seq)                     ||
11888        seq->record_imseq == NULL           ||
11889        seq->record_imarr == NULL           ||
11890        IMARR_COUNT(seq->record_imarr) == 0   ) EXRETURN ;
11891 
11892    ntot = IMARR_COUNT(seq->record_imarr) ;
11893 
11894         if( npos <  0    ) npos = 0 ;
11895    else if( npos >= ntot ) npos = ntot-1 ;
11896 
11897    drive_MCW_imseq( seq->record_imseq , isqDR_newseq , seq ) ;
11898 
11899 #ifndef DONT_ONOFF_ONE
11900    if( ntot == 1 )
11901       drive_MCW_imseq( seq->record_imseq,isqDR_onoffwid,(XtPointer)ITOP(isqDR_offwid));
11902    else
11903       drive_MCW_imseq( seq->record_imseq,isqDR_onoffwid,(XtPointer)ITOP(isqDR_onwid) );
11904 #endif
11905 
11906    drive_MCW_imseq( seq->record_imseq , isqDR_reimage , (XtPointer)ITOP(npos) ) ;
11907 
11908    EXRETURN ;
11909 }
11910 
11911 /*------------------------------------------------------------------
11912    Routine to provide data to the recording imseq.
11913    Just returns the control information, or the selected image.
11914 --------------------------------------------------------------------*/
11915 
ISQ_record_getim(int n,int type,XtPointer handle)11916 XtPointer ISQ_record_getim( int n , int type , XtPointer handle )
11917 {
11918    int ntot = 0 ;
11919    MCW_imseq *seq = (MCW_imseq *) handle ;  /* parent of recorder */
11920 
11921 ENTRY("ISQ_record_getim") ;
11922 
11923    if( seq->record_imarr != NULL ) ntot = IMARR_COUNT(seq->record_imarr) ;
11924    if( ntot < 1 ) ntot = 1 ;
11925 
11926    /*--- send control info ---*/
11927 
11928    if( type == isqCR_getstatus ){
11929       MCW_imseq_status *stat = myXtNew( MCW_imseq_status ); /* will be free-d */
11930                                                             /* when imseq is */
11931                                                             /* destroyed    */
11932       stat->num_total  = ntot ;
11933       stat->num_series = stat->num_total ;
11934       stat->send_CB    = ISQ_record_send_CB ;
11935       stat->parent     = NULL ;
11936       stat->aux        = NULL ;
11937 
11938       stat->transforms0D = NULL ;
11939       stat->transforms2D = NULL ;
11940 
11941       RETURN( (XtPointer)stat ) ;
11942    }
11943 
11944    /*--- overlay [05 Jan 2005] ---*/
11945 
11946    if( type == isqCR_getoverlay ) RETURN(NULL) ;  /* no image overlay */
11947 
11948    if( type == isqCR_getmemplot ){                /* graphics overlay */
11949      MEM_plotdata *mp ;
11950      if( seq->record_mplot == NULL ) RETURN(NULL) ;
11951      if( n < 0 ) n = 0 ; else if( n >= ntot ) n = ntot-1 ;
11952      mp = copy_memplot( seq->record_mplot[n] ) ;
11953      RETURN( (XtPointer)mp ) ;   /* may be NULL */
11954    }
11955 
11956    /*--- return a copy of a recorded image
11957          (since the imseq will delete it when it is done) ---*/
11958 
11959    if( type == isqCR_getimage || type == isqCR_getqimage ){
11960       MRI_IMAGE *im = NULL , *rim ;
11961 
11962       if( seq->record_imarr != NULL ){
11963          if( n < 0 ) n = 0 ; else if( n >= ntot ) n = ntot-1 ;
11964          rim = IMARR_SUBIMAGE(seq->record_imarr,n) ;
11965          if( rim != NULL ) im = mri_to_rgb( rim ) ;
11966       }
11967       RETURN( (XtPointer)im ) ;
11968    }
11969 
11970    RETURN( NULL ) ; /* should not occur, but who knows? */
11971 }
11972 
11973 /*---------------------------------------------------------------------------
11974    Routine called when the recording imseq wants to send a message.
11975    In this case, all we need to handle is the destroy message,
11976    so that we can free some memory.
11977 -----------------------------------------------------------------------------*/
11978 
ISQ_record_send_CB(MCW_imseq * seq,XtPointer handle,ISQ_cbs * cbs)11979 void ISQ_record_send_CB( MCW_imseq *seq , XtPointer handle , ISQ_cbs *cbs )
11980 {
11981 ENTRY("ISQ_record_send_CB") ;
11982 
11983    switch( cbs->reason ){
11984 
11985       case isqCR_destroy:{
11986          MCW_imseq *pseq = (MCW_imseq *) seq->parent ;
11987 
11988          /* turn off recording in the parent */
11989 
11990          pseq->record_imseq = NULL ;
11991          if( pseq->record_mplot != NULL && pseq->record_imarr != NULL ){
11992            int ib ;
11993            for( ib=0 ; ib < IMARR_COUNT(pseq->record_imarr) ; ib++ )
11994              delete_memplot( pseq->record_mplot[ib] ) ;
11995            free((void *)pseq->record_mplot) ; pseq->record_mplot = NULL ;
11996          }
11997          if( pseq->record_imarr != NULL ) DESTROY_IMARR(pseq->record_imarr) ;
11998          if( RECORD_ISON(pseq->record_status) ){
11999             pseq->record_status = RECORD_STATUS_OFF ;
12000             MCW_set_bbox( pseq->record_status_bbox , RECORD_STATUS_OFF ) ;
12001             MCW_invert_widget( pseq->record_cbut ) ;
12002          }
12003 
12004          /* can now clean out the recording imseq */
12005 
12006          myXtFree(seq->status) ; myXtFree(seq) ;
12007       }
12008       break ;
12009 
12010    }
12011 
12012    EXRETURN ;
12013 }
12014 
12015 /*----------------------------------------------------------------------------*/
12016 
ISQ_record_kill_CB(Widget w,XtPointer client_data,XtPointer call_data)12017 void ISQ_record_kill_CB( Widget w, XtPointer client_data, XtPointer call_data )
12018 {
12019    MCW_imseq *seq = (MCW_imseq *) client_data ;
12020    MCW_imseq *pseq ;
12021    int pos=-1 ;
12022 
12023 ENTRY("ISQ_record_kill_CB") ;
12024 
12025    if( !ISQ_REALZ(seq) || !seq->record_mode ) EXRETURN ; /* bad */
12026 
12027    pseq = (MCW_imseq *) seq->parent ;  /* the one driving this recorder */
12028 
12029    if( pseq->record_imarr == NULL ) EXRETURN ; /* bad */
12030 
12031    drive_MCW_imseq( seq , isqDR_getimnr, (XtPointer)&pos ) ; /* where am us? */
12032 
12033    if( pos < 0 || pos >= IMARR_COUNT(pseq->record_imarr) ) EXRETURN ;
12034 
12035    /* empty out the image in the recorded sequence */
12036 
12037    mri_free( IMARR_SUBIM(pseq->record_imarr,pos) ) ;
12038    IMARR_SUBIM(pseq->record_imarr,pos) = NULL ;
12039    delete_memplot( pseq->record_mplot[pos] ) ;  /* 05 Jan 2005 */
12040    pseq->record_mplot[pos] = NULL ;
12041 
12042    ISQ_redisplay( seq , -1 , isqDR_display ) ;  /* show the empty image */
12043 
12044    EXRETURN ;
12045 }
12046 
12047 /*---------------------------------------------------------------------
12048    Handle the user's action on the Button 3 popup on the Save: button
12049 -----------------------------------------------------------------------*/
12050 
ISQ_butsave_choice_CB(Widget w,XtPointer client_data,MCW_choose_cbs * cbs)12051 void ISQ_butsave_choice_CB( Widget w , XtPointer client_data ,
12052                                        MCW_choose_cbs *cbs   )
12053 {
12054    MCW_imseq *seq = (MCW_imseq *) client_data ;
12055    int pp , agif_ind=0 , mpeg_ind=0 , nstr ;
12056 
12057    if( !ISQ_REALZ(seq)               ||
12058        cbs->reason != mcwCR_integer  ||
12059        seq->dialog_starter==NBUT_DISP  ){  /* bad things */
12060 
12061       XBell(XtDisplay(w),100); POPDOWN_strlist_chooser ; return ;
12062    }
12063 
12064    nstr = ppmto_num+1 ;
12065    if( ppmto_agif_filter != NULL ) agif_ind = nstr++ ;
12066    if( ppmto_mpeg_filter != NULL ) mpeg_ind = nstr++ ;
12067 
12068    seq->opt.save_nsize = seq->opt.save_pnm
12069                        = seq->opt.save_agif = seq->opt.save_mpeg = 0 ;
12070 
12071    pp = cbs->ival ;
12072         if( pp == 0         ) seq->opt.save_filter=-1  ; /* Save:bkg */
12073    else if( pp <= ppmto_num ) seq->opt.save_filter=pp-1; /* Save.typ */
12074    else if( pp == agif_ind  ) seq->opt.save_agif  = 1  ; /* Sav:aGif */
12075    else if( pp == mpeg_ind  ) seq->opt.save_mpeg  = 1  ; /* Sav:mpeg */
12076 
12077    if( ppmto_agif_filter == NULL ) seq->opt.save_agif = 0 ;  /* 07 Apr 2005 */
12078    if( ppmto_mpeg_filter == NULL ) seq->opt.save_mpeg = 0 ;
12079 
12080    SET_SAVE_LABEL(seq) ; return ;
12081 }
12082 
12083 /*--------------------------------------------------------------------
12084   make Button 3 popup for Save button
12085   -- 27 Jul 2001: add stuff for animated GIF (ppmto_num+1 index)
12086 ----------------------------------------------------------------------*/
12087 
ISQ_butsave_EV(Widget w,XtPointer client_data,XEvent * ev,RwcBoolean * continue_to_dispatch)12088 void ISQ_butsave_EV( Widget w , XtPointer client_data ,
12089                      XEvent *ev , RwcBoolean *continue_to_dispatch )
12090 {
12091    MCW_imseq *seq = (MCW_imseq *) client_data ;
12092 
12093    if( !ISQ_REALZ(seq) ) return ;
12094 
12095    ISQ_timer_stop(seq) ;
12096 
12097    switch( ev->type ){
12098       case ButtonPress:{
12099          XButtonEvent *event = (XButtonEvent *) ev ;
12100          if( event->button == Button3 ){
12101             char **strlist ; int pp , nstr , agif_ind=0 , mpeg_ind=0 ;
12102             if( seq->dialog_starter==NBUT_DISP ){XBell(XtDisplay(w),100); return; }
12103             strlist = (char **) malloc(sizeof(char *)*(ppmto_num+3)) ;
12104             strlist[0] = strdup("Save:bkg") ;             /* special case */
12105             for( pp=0 ; pp < ppmto_num ; pp++ ){          /* filters */
12106                strlist[pp+1] = AFMALL( char, 16) ;
12107                sprintf(strlist[pp+1],"Save.%.3s",ppmto_suffix[pp]) ;
12108             }
12109             nstr = ppmto_num+1 ;
12110             if( ppmto_agif_filter != NULL ){
12111                agif_ind = nstr ;
12112                strlist[nstr++] = strdup("Sav:aGif") ;     /* special case */
12113             }
12114             if( ppmto_mpeg_filter != NULL ){
12115                mpeg_ind = nstr ;
12116                strlist[nstr++] = strdup("Sav:mpeg") ;     /* special case */
12117             }
12118                  if(seq->opt.save_agif && agif_ind > 0 ) pp=agif_ind ;
12119             else if(seq->opt.save_mpeg && mpeg_ind > 0 ) pp=mpeg_ind ;
12120             else if(seq->opt.save_filter < 0)            pp=0        ;
12121             else                                     pp=seq->opt.save_filter+1 ;
12122             MCW_choose_strlist( w , "Image Save format" ,
12123                                 nstr , pp , strlist ,
12124                                 ISQ_butsave_choice_CB , (XtPointer) seq ) ;
12125             for( pp=0 ; pp < nstr ; pp++ ) free(strlist[pp]) ;
12126             free(strlist) ;
12127          } else if( event->button == Button2 ){
12128             XBell(XtDisplay(w),100) ;
12129             MCW_popup_message( w, " \n Ouch! \n ", MCW_USER_KILL|MCW_QUICK_KILL );
12130             /** AFNI_speak( "Ouch!" , 0 ) ; **/
12131          }
12132       }
12133       break ;
12134    }
12135    return ;
12136 }
12137 
12138 /*--------------------------------------------------------------------------*/
12139 /*! Get the label for overlay. [11 Jun 2002]
12140 ----------------------------------------------------------------------------*/
12141 
ISQ_getlabel(int nn,MCW_imseq * seq)12142 char * ISQ_getlabel( int nn , MCW_imseq *seq )
12143 {
12144    char *lab=NULL , *labadd=NULL ;
12145 
12146 ENTRY("ISQ_getlabel") ;
12147 
12148 #if 0
12149    lab = (char *) seq->getim( nn,isqCR_getlabel,seq->getaux );
12150 #else
12151    AFNI_CALL_VALU_3ARG( seq->getim , char *,lab ,
12152                         int,nn , int,isqCR_getlabel , XtPointer,seq->getaux ) ;
12153 #endif
12154 
12155    /* 23 Dec 2011: stuff to append to the label */
12156 
12157    labadd = seq->overlay_label ;
12158 
12159    if( labadd == NULL || *labadd == '\0' )
12160      labadd = getenv("AFNI_IMAGE_LABEL_STRING") ;
12161 
12162    if( labadd != NULL && *labadd != '\0' ){
12163      if( lab == NULL ) lab = strdup(labadd) ;
12164      else {
12165        lab = (char *)realloc(lab,sizeof(char)*(strlen(lab)+strlen(labadd)+4)) ;
12166        strcat(lab,labadd) ;
12167      }
12168    }
12169 
12170    RETURN(lab) ;
12171 }
12172 
12173 /*--------------------------------------------------------------------------*/
12174 /*! Get the memplot for overlay. [11 Jun 2002]
12175 ----------------------------------------------------------------------------*/
12176 
ISQ_getmemplot(int nn,MCW_imseq * seq)12177 MEM_plotdata * ISQ_getmemplot( int nn , MCW_imseq *seq )
12178 {
12179    MEM_plotdata *mp=NULL ;
12180    int           ntic ;
12181 
12182 ENTRY("ISQ_getmemplot") ;
12183 
12184 #if 0
12185    mp = (MEM_plotdata *) seq->getim( nn,isqCR_getmemplot,seq->getaux );
12186 #else
12187    AFNI_CALL_VALU_3ARG( seq->getim , MEM_plotdata *,mp ,
12188                         int,nn , int,isqCR_getmemplot , XtPointer,seq->getaux ) ;
12189 #endif
12190 
12191    if( mp != NULL && seq->cropit ){  /* scale memplot for cropping region */
12192      float sx,sy,tx,ty ;
12193      float xa=seq->crop_xa, xb=seq->crop_xb, ya=seq->crop_ya, yb=seq->crop_yb ;
12194      float nxorg=seq->crop_nxorg , nyorg=seq->crop_nyorg ;
12195      MEM_plotdata *np ;
12196 
12197      if( xb >= nxorg ) xb = nxorg-1 ;
12198      if( yb >= nyorg ) yb = nyorg-1 ;
12199 
12200      /**
12201       Original plot has [0..1]x[0..1] mapped to [0..nxorg]x[nyorg..0].
12202       Now, image will be cropped to [xa..xb]x[ya..yb], which will be
12203       mapped from plot coords [0..1]x[1..0].  So we need to transform
12204       plot coords so that the new
12205            x_plot=0 is at x_image=xa
12206            x_plot=1 is at x_image=xb
12207            y_plot=0 is at y_image=yb
12208            y_plot=1 is at y_image=ya
12209 
12210       Input:   x_plot  = x_image / nxorg
12211                y_plot  = 1 - y_image / nyorg
12212 
12213       Output:  x_plot' = sx * x_plot + tx   > This is done in
12214                y_plot' = sy * y_plot + ty   > scale_memplot function
12215 
12216       Find sx,tx so that x_plot'[x_image=xa  ]=0 and x_plot'[x_image=xb+1]=1.
12217       Find sy,ty so that y_plot'[y_image=yb+1]=0 and y_plot'[y_image=ya  ]=1.
12218      **/
12219 
12220      sx = nxorg / (xb+1-xa) ;
12221      tx = -sx * xa / nxorg ;
12222 
12223      sy = nyorg / (yb+1-ya) ;
12224      ty = -sy * (1.0 - (yb+1) / nyorg) ;
12225 
12226      scale_memplot( sx,tx , sy,ty , 1.0 , mp ) ;    /* expand scale  */
12227      np = clip_memplot( 0.0,0.0 , 1.0,1.0 , mp ) ;  /* clip to window */
12228      DESTROY_MEMPLOT(mp) ; mp = np ;
12229    }
12230 
12231    /*** 23 Feb 2004: tick marks around the edge of the image? ***/
12232 
12233    ntic = seq->wbar_ticnum_av->ival ;
12234 
12235    if( ntic > 0 ){
12236      MEM_plotdata *tp ;
12237      char *eee ;
12238      /* float tic, fac=1.0/ntic ; */
12239      float rr=0.8,gg=1.0,bb=0.6 , tic, xfac, yfac;
12240      float xlen = 0.0, ylen = 0.0;  /* when ntics is in mm */
12241      int it, nticx, nticy;
12242 
12243      /* plot ntic as separation distance for J Binder  23 Feb 2006 [rickr] */
12244 
12245      if( seq->imim && AFNI_yesenv("AFNI_IMAGE_TICK_DIV_IN_MM") ){
12246          /* get image size */
12247          if( mp != NULL && seq->cropit ){  /* cropped size */
12248            xlen = abs(seq->crop_xb - seq->crop_xa);
12249            ylen = abs(seq->crop_yb - seq->crop_ya);
12250          } else {                          /* full size */
12251            xlen = seq->imim->nx * seq->imim->dx;
12252            ylen = seq->imim->ny * seq->imim->dy;
12253          }
12254 
12255          nticx = xlen/ntic;
12256          nticy = ylen/ntic;
12257          xfac=ntic/xlen;
12258          yfac=ntic/ylen;
12259      } else {
12260          nticx = nticy = ntic;
12261          xfac=1.0/ntic;
12262          yfac=1.0/ntic;
12263      }
12264 
12265      create_memplot_surely( "Iticplot" , 1.0 ) ;
12266      set_thick_memplot(0.0) ;
12267      eee = getenv("AFNI_IMAGE_LABEL_COLOR") ;
12268      if( eee != NULL )
12269        DC_parse_color( seq->dc , eee , &rr,&gg,&bb ) ;
12270      set_color_memplot(rr,gg,bb) ;
12271 
12272      tic = 0.01 * seq->wbar_ticsiz_av->ival ;  /* percent of image size */
12273 
12274      /* x and y are separate, in case ntic is in mm */
12275      for( it=0 ; it <= nticy ; it++ ){
12276        plotpak_line( 0.0,it*yfac , tic    ,it*yfac ) ;
12277        plotpak_line( 1.0,it*yfac , 1.0-tic,it*yfac ) ;
12278      }
12279      for( it=0 ; it <= nticx ; it++ ){
12280        plotpak_line( it*xfac,0.0 , it*xfac ,tic    ) ;
12281        plotpak_line( it*xfac,1.0 , it*xfac ,1.0-tic) ;
12282      }
12283 
12284      /* append tick plot to existing plot, if any */
12285 
12286      tp = get_active_memplot() ;
12287      if( mp != NULL ){ append_to_memplot(mp,tp); delete_memplot(tp); }
12288      else              mp = tp ;
12289    }
12290 
12291    RETURN(mp) ;
12292 }
12293 
12294 /*--------------------------------------------------------------------------*/
12295 /*! Get the image for overlay. [11 Jun 2002]
12296 ----------------------------------------------------------------------------*/
12297 
ISQ_getoverlay(int nn,MCW_imseq * seq)12298 MRI_IMAGE * ISQ_getoverlay( int nn , MCW_imseq *seq )
12299 {
12300    MRI_IMAGE *tim=NULL ;
12301 
12302 ENTRY("ISQ_getoverlay") ;
12303 
12304 #if 0
12305    tim = (MRI_IMAGE *) seq->getim( nn , isqCR_getoverlay , seq->getaux ) ;
12306 #else
12307    AFNI_CALL_VALU_3ARG( seq->getim , MRI_IMAGE *,tim ,
12308                         int,nn , int,isqCR_getoverlay , XtPointer,seq->getaux ) ;
12309 #endif
12310 
12311    if( tim == NULL ) RETURN(NULL) ;
12312 
12313    MRI_floatscan(tim) ; /* 10 Jun 2021 */
12314 
12315    /*--- cut out cropped region, if any ---*/
12316 
12317    if( seq->cropit ){
12318      MRI_IMAGE *qim = mri_cut_2D( tim, seq->crop_xa,seq->crop_xb,
12319                                        seq->crop_ya,seq->crop_yb ) ;
12320      if( qim != NULL ){ mri_free(tim); tim = qim; }
12321    }
12322 
12323    RETURN(tim) ;
12324 }
12325 
12326 /*--------------------------------------------------------------------------*/
12327 /*! Get the image for display.  Maybe use projections. [31 Jan 2002] */
12328 
ISQ_getimage(int nn,MCW_imseq * seq)12329 MRI_IMAGE * ISQ_getimage( int nn , MCW_imseq *seq )
12330 {
12331    int ii , rr , jj , ns , npix , ktim ;
12332    MRI_IMAGE *tim=NULL , *qim=NULL , *fim=NULL ;
12333    MRI_IMARR *imar ;
12334    float *far , val=0.0f , *qar , **iar ;
12335 
12336 ENTRY("ISQ_getimage") ;
12337 
12338    /* get the commanded slice */
12339 
12340 #if 0
12341    tim = (MRI_IMAGE *) seq->getim( nn, isqCR_getimage, seq->getaux ) ;
12342 #else
12343    AFNI_CALL_VALU_3ARG( seq->getim , MRI_IMAGE *,tim ,
12344                         int,nn , int,isqCR_getimage , XtPointer,seq->getaux ) ;
12345 #endif
12346 
12347    if( tim == NULL ) RETURN(NULL) ;
12348 
12349    MRI_floatscan(tim) ; /* 10 Jun 2021 */
12350 
12351    if( seq->cropit ){
12352 
12353      if( seq->crop_nxorg < 0 || seq->crop_nyorg < 0 ){ /* orig image size not set yet */
12354        seq->crop_nxorg = tim->nx ;
12355        seq->crop_nyorg = tim->ny ;
12356      }
12357 
12358      if( tim->nx != seq->crop_nxorg ||    /* image changed size? */
12359          tim->ny != seq->crop_nyorg   ){  /* => turn cropping off */
12360 
12361        seq->cropit = 0 ; seq->crop_nxorg = seq->crop_nyorg = -1 ;
12362 
12363        if( seq->crop_drag ){              /* should not happen */
12364          MCW_invert_widget( seq->crop_drag_pb ) ;
12365          seq->crop_drag = 0 ;
12366        }
12367 
12368      } else {
12369        MRI_IMAGE *cim ;
12370        if( seq->crop_xb >= seq->crop_nxorg ) seq->crop_xb = seq->crop_nxorg - 1 ;
12371        if( seq->crop_yb >= seq->crop_nyorg ) seq->crop_yb = seq->crop_nyorg - 1 ;
12372        cim = mri_cut_2D( tim, seq->crop_xa,seq->crop_xb,
12373                               seq->crop_ya,seq->crop_yb ) ;
12374        if( cim != NULL ){ mri_free(tim); tim = cim; }
12375      }
12376    }
12377 
12378    /* the old way - return this slice */
12379 
12380    if( !ISQ_DOING_SLICE_PROJ(seq) ) RETURN(tim) ;
12381 
12382    ns = seq->status->num_series ;
12383    rr = seq->slice_proj_range   ; if( rr > ns/2 ) rr = ns/2 ;
12384 
12385    if( rr                    == 0           ||
12386        seq->slice_proj_index == 0           ||
12387        seq->slice_proj_func  == NULL        ||
12388        tim                   == NULL        ||
12389        tim->kind             == MRI_rgb     ||
12390        tim->kind             == MRI_complex   ){
12391 
12392       RETURN(tim) ;
12393    }
12394 
12395    /* the new way - return the projection of a bunch of images */
12396 
12397    INIT_IMARR(imar) ;
12398 
12399    ktim = tim->kind ;  /* save for later use */
12400 
12401    /* get the images into imar */
12402 
12403    for( ii=-rr ; ii <= rr ; ii++ ){
12404 
12405       if( ii == 0 ){                /* at the middle, just put a   */
12406          fim = mri_to_float(tim) ;  /* copy of the commanded slice */
12407          ADDTO_IMARR(imar,fim) ;
12408          continue ;
12409       }
12410 
12411       jj = nn+ii ;                    /* offset slice */
12412            if( jj < 0   ) jj = 0    ; /* but not past the edges */
12413       else if( jj >= ns ) jj = ns-1 ;
12414 
12415 #if 0
12416       qim = (MRI_IMAGE *) seq->getim( jj, isqCR_getimage, seq->getaux ) ;
12417 #else
12418       AFNI_CALL_VALU_3ARG( seq->getim , MRI_IMAGE *,qim ,
12419                            int,jj , int,isqCR_getimage , XtPointer,seq->getaux ) ;
12420 #endif
12421 
12422       if( qim == NULL )
12423          fim = mri_to_float(tim) ;                 /* need something */
12424       else if( qim->kind != MRI_float ){
12425          fim = mri_to_float(qim) ; mri_free(qim) ; /* convert it */
12426       } else
12427          fim = qim ;                               /* just put it here */
12428 
12429       if( seq->cropit ){
12430         MRI_IMAGE *cim = mri_cut_2D( fim , seq->crop_xa,seq->crop_xb,
12431                                            seq->crop_ya,seq->crop_yb ) ;
12432         if( cim != NULL ){ mri_free(fim); fim = cim; }
12433       }
12434 
12435       ADDTO_IMARR(imar,fim) ;
12436    }
12437 
12438    /* project images, put results into qim */
12439 
12440    qim = mri_new_conforming( tim , MRI_float ) ;
12441    qar = MRI_FLOAT_PTR(qim) ; MRI_COPY_AUX(qim,tim) ;
12442    mri_free(tim) ;
12443 
12444    npix = qim->nvox ;
12445    rr   = 2*rr+1 ;
12446    far  = (float * ) malloc( sizeof(float  ) * rr ) ;
12447    iar  = (float **) malloc( sizeof(float *) * rr ) ;
12448 
12449    for( ii=0 ; ii < rr ; ii++ )
12450      iar[ii] = MRI_FLOAT_PTR(IMARR_SUBIM(imar,ii)) ;
12451 
12452    for( jj=0 ; jj < npix ; jj++ ){
12453 
12454      for( ii=0 ; ii < rr ; ii++ ) far[ii] = iar[ii][jj] ;
12455 
12456 #if 0
12457      val = seq->slice_proj_func( rr , far ) ;
12458 #else
12459      AFNI_CALL_proj_function( seq->slice_proj_func , rr,far , val ) ;
12460 #endif
12461 
12462      qar[jj] = val ;
12463    }
12464 
12465    free(iar) ; free(far) ; DESTROY_IMARR(imar) ;
12466 
12467    if( ktim != MRI_float ){
12468      tim = mri_to_mri(ktim,qim); mri_free(qim); qim = tim;
12469    }
12470 
12471    RETURN(qim) ;
12472 }
12473 
12474 /*---------------------------------------------------------------------*/
12475 
ISQ_cropim(MRI_IMAGE * tim,MCW_imseq * seq)12476 MRI_IMAGE * ISQ_cropim( MRI_IMAGE *tim , MCW_imseq *seq )
12477 {
12478    if( tim == NULL || !seq->cropit ) return NULL ;
12479 
12480    if( seq->crop_nxorg < 0 || seq->crop_nyorg < 0 ){ /* orig image size not set yet */
12481      seq->crop_nxorg = tim->nx ;
12482      seq->crop_nyorg = tim->ny ;
12483    }
12484 
12485    if( tim->nx != seq->crop_nxorg ||    /* image changed size? */
12486        tim->ny != seq->crop_nyorg   ){  /* => turn cropping off */
12487 
12488      seq->cropit = 0 ; seq->crop_nxorg = seq->crop_nyorg = -1 ;
12489 
12490      if( seq->crop_drag ){              /* should not happen */
12491        MCW_invert_widget( seq->crop_drag_pb ) ;
12492        seq->crop_drag = 0 ;
12493      }
12494 
12495    } else {
12496      MRI_IMAGE *cim ;
12497      if( seq->crop_xb >= seq->crop_nxorg ) seq->crop_xb = seq->crop_nxorg - 1 ;
12498      if( seq->crop_yb >= seq->crop_nyorg ) seq->crop_yb = seq->crop_nyorg - 1 ;
12499      cim = mri_cut_2D( tim, seq->crop_xa,seq->crop_xb,
12500                             seq->crop_ya,seq->crop_yb ) ;
12501      if( cim != NULL ){ MRI_COPY_AUX(cim,tim); return cim; }
12502    }
12503 
12504    return NULL ;
12505 }
12506 
12507 /*---------------------------------------------------------------------*/
12508 
ISQ_get_improj(int nn,MCW_imseq * seq,int getcode)12509 MRI_IMAGE * ISQ_get_improj( int nn , MCW_imseq *seq , int getcode )
12510 {
12511    MRI_IMAGE *tim=NULL , *cim ;
12512    int ii , rr , jj , ns , npix , ktim ;
12513    MRI_IMAGE *qim=NULL , *fim=NULL ;
12514    MRI_IMARR *imar ;
12515    float *far , val=0.0f , *qar , **iar ;
12516 
12517 ENTRY("ISQ_get_improj") ;
12518 
12519    /* get central slice */
12520 
12521    AFNI_CALL_VALU_3ARG( seq->getim , MRI_IMAGE *,tim ,
12522                         int,nn , int,getcode , XtPointer,seq->getaux ) ;
12523 
12524    if( tim == NULL ) RETURN(NULL) ;  /* should not happen */
12525 
12526    cim = ISQ_cropim(tim,seq) ; if( cim != NULL ){ mri_free(tim); tim=cim; }
12527 
12528    /* return just this slice? */
12529 
12530    if( !ISQ_DOING_SLICE_PROJ(seq) ) RETURN(tim) ;
12531 
12532    ns = seq->status->num_series ;
12533    rr = seq->slice_proj_range   ; if( rr > ns/2 ) rr = ns/2 ;
12534 
12535    if( rr                    == 0           ||
12536        seq->slice_proj_index == 0           ||
12537        seq->slice_proj_func  == NULL        ||
12538        tim                   == NULL        ||
12539        tim->kind             == MRI_rgb     ||
12540        tim->kind             == MRI_complex   ){
12541 
12542       RETURN(tim) ;
12543    }
12544 
12545    /* return the projection of a bunch of images */
12546 
12547    INIT_IMARR(imar) ;
12548 
12549    ktim = tim->kind ;  /* save for later use */
12550 
12551    /* get the images into imar */
12552 
12553 STATUS("projection loop") ;
12554 
12555    for( ii=-rr ; ii <= rr ; ii++ ){
12556 
12557       if( ii == 0 ){                /* at the middle, just put a   */
12558          fim = mri_to_float(tim) ;  /* copy of the commanded slice */
12559          ADDTO_IMARR(imar,fim) ;
12560          continue ;
12561       }
12562 
12563       jj = nn+ii ;                    /* offset slice */
12564            if( jj < 0   ) jj = 0    ; /* but not past the edges */
12565       else if( jj >= ns ) jj = ns-1 ;
12566 
12567 STATUS("call getim") ;
12568 
12569       AFNI_CALL_VALU_3ARG( seq->getim , MRI_IMAGE *,qim ,
12570                            int,jj , int,getcode , XtPointer,seq->getaux ) ;
12571 
12572       if( qim == NULL )
12573          fim = mri_to_float(tim) ;                 /* need something */
12574       else if( qim->kind != MRI_float ){
12575          fim = mri_to_float(qim) ; mri_free(qim) ; /* convert it */
12576       } else
12577          fim = qim ;                               /* just put it here */
12578 
12579       cim = ISQ_cropim(fim,seq) ; if( cim != NULL ){ mri_free(fim); fim=cim; }
12580       ADDTO_IMARR(imar,fim) ;
12581    }
12582 
12583    /* project images, put results into qim */
12584 
12585    qim = mri_new_conforming( tim , MRI_float ) ;
12586    qar = MRI_FLOAT_PTR(qim) ; MRI_COPY_AUX(qim,tim) ;
12587    mri_free(tim) ; tim = NULL ;
12588 
12589    npix = qim->nvox ;
12590    rr   = 2*rr+1 ;
12591    far  = (float * )malloc( sizeof(float  ) * rr ) ;
12592    iar  = (float **)malloc( sizeof(float *) * rr ) ;
12593 
12594    for( ii=0 ; ii < rr ; ii++ )
12595      iar[ii] = MRI_FLOAT_PTR(IMARR_SUBIM(imar,ii)) ;
12596 
12597    for( jj=0 ; jj < npix ; jj++ ){
12598      for( ii=0 ; ii < rr ; ii++ ) far[ii] = iar[ii][jj] ;
12599      AFNI_CALL_proj_function( seq->slice_proj_func , rr,far , val ) ;
12600      qar[jj] = val ;
12601    }
12602 
12603    free(iar) ; free(far) ; DESTROY_IMARR(imar) ;
12604 
12605    if( ktim != MRI_float ){
12606      tim = mri_to_mri(ktim,qim); mri_free(qim); qim = tim;
12607    }
12608 
12609    RETURN(qim) ;
12610 }
12611 
12612 /*---------------------------------------------------------------------*/
12613 
ISQ_getulay(int nn,MCW_imseq * seq)12614 MRI_IMAGE * ISQ_getulay( int nn , MCW_imseq *seq )
12615 {
12616    MRI_IMAGE *tim=NULL , *cim=NULL ;
12617 
12618 ENTRY("ISQ_getulay") ;
12619 
12620 #if 0
12621    AFNI_CALL_VALU_3ARG( seq->getim , MRI_IMAGE *,tim ,
12622                         int,nn , int,isqCR_getulayim , XtPointer,seq->getaux ) ;
12623 
12624    cim = ISQ_cropim( tim , seq ) ;
12625    if( cim != NULL ){ mri_free(tim) ; tim = cim ; }
12626 #else
12627    tim = ISQ_get_improj( nn , seq , isqCR_getulayim ) ;
12628 #endif
12629    RETURN(tim) ;
12630 }
12631 
12632 /*---------------------------------------------------------------------*/
12633 
ISQ_getolay(int nn,MCW_imseq * seq)12634 MRI_IMAGE * ISQ_getolay( int nn , MCW_imseq *seq )
12635 {
12636    MRI_IMAGE *tim=NULL , *cim ;
12637 
12638 ENTRY("ISQ_getolay") ;
12639 
12640    AFNI_CALL_VALU_3ARG( seq->getim , MRI_IMAGE *,tim ,
12641                         int,nn , int,isqCR_getolayim , XtPointer,seq->getaux ) ;
12642 
12643    cim = ISQ_cropim( tim , seq ) ;
12644    if( cim != NULL ){ mri_free(tim) ; tim = cim ; }
12645    RETURN(tim) ;
12646 }
12647 
12648 /*---------------------------------------------------------------------*/
12649 
ISQ_getchecked(int nn,MCW_imseq * seq)12650 MRI_IMAGE *ISQ_getchecked( int nn , MCW_imseq *seq )
12651 {
12652    MRI_IMAGE *qim=NULL , *uim , *oim ; float dx,dy ;
12653 
12654 ENTRY("ISQ_getchecked") ;
12655 
12656    qim = ISQ_getulay(nn,seq) ; if( qim == NULL ) RETURN(NULL) ;
12657    dx  = qim->dx ; dy = qim->dy ;
12658    uim = ISQ_process_mri(nn,seq,qim,0) ; mri_free(qim) ;
12659 
12660    qim = ISQ_getolay(nn,seq) ; if( qim == NULL ) RETURN(uim) ;
12661    oim = ISQ_process_mri(nn,seq,qim,PFLAG_NOTHING) ; mri_free(qim) ;
12662 
12663    if( uim->kind == MRI_rgb && oim->kind == MRI_short ){
12664      qim = ISQ_index_to_rgb( seq->dc , 0 , oim ) ;
12665      mri_free(oim) ; oim = qim ;
12666    } else if( uim->kind == MRI_short && oim->kind == MRI_rgb ){
12667      qim = ISQ_index_to_rgb( seq->dc , 0 , uim ) ;
12668      mri_free(uim) ; uim = qim ;
12669    }
12670 
12671    if( seq->render_mode == RENDER_CHECK_OU )
12672      qim = mri_check_2D( seq->wbar_checkbrd_av->ival , oim , uim ) ;
12673    else if( seq->render_mode == RENDER_CHECK_UO )
12674      qim = mri_check_2D( seq->wbar_checkbrd_av->ival , uim , oim ) ;
12675    else if( seq->render_mode == RENDER_WIPE_LEFT )    /* WIPE stuff 22 Aug 2014 */
12676      qim = mri_wiper_2D( WIPER_FROM_LEFT   , seq->render_fac , oim,uim ) ;
12677    else if( seq->render_mode == RENDER_WIPE_BOT )
12678      qim = mri_wiper_2D( WIPER_FROM_BOTTOM , seq->render_fac , oim,uim ) ;
12679    else if( seq->render_mode == RENDER_MIX )
12680      qim = mri_mix_2D  (                     seq->render_fac , uim,oim ) ;
12681    else if( seq->render_mode == RENDER_WIPE_RIGHT )
12682      qim = mri_wiper_2D( WIPER_FROM_LEFT   , seq->render_fac , uim,oim ) ;
12683    else if( seq->render_mode == RENDER_WIPE_TOP )
12684      qim = mri_wiper_2D( WIPER_FROM_BOTTOM , seq->render_fac , uim,oim ) ;
12685 
12686    mri_free(oim) ;
12687    if( qim == NULL ){ uim->dx = dx ; uim->dy = dy ; RETURN(uim) ; }
12688 
12689    mri_free(uim) ;    qim->dx = dx ; qim->dy = dy ; RETURN(qim) ;
12690 }
12691 
12692 /*--------------------------------------------------------------------*/
12693 
ISQ_set_scale(Widget wscal,int percent)12694 void ISQ_set_scale( Widget wscal , int percent )
12695 {
12696    int val , old ;
12697 
12698    val = percent ;
12699    if( wscal == NULL || val < 0 || val > 100 ) return ;
12700    XmScaleGetValue( wscal , &old ) ; if( val == old ) return ;
12701    XtVaSetValues( wscal , XmNvalue , val , NULL ) ;
12702    XmUpdateDisplay(wscal) ;
12703    return ;
12704 }
12705 
12706 /*---------------------------------------------------------------------*/
12707 
ISQ_popdown_render_scal(MCW_imseq * seq)12708 void ISQ_popdown_render_scal( MCW_imseq *seq )
12709 {
12710    if( seq->render_scal != NULL ) XtUnmanageChild( seq->render_scal ) ;
12711    return ;
12712 }
12713 
12714 /*---------------------------------------------------------------------*/
12715 
ISQ_popup_render_scal(MCW_imseq * seq)12716 void ISQ_popup_render_scal( MCW_imseq *seq )
12717 {
12718 #undef  NCOL
12719 #define NCOL 30
12720    static char *cname[] = {
12721       "#0000ff", "#3300ff", "#6600ff", "#9900ff", "#cc00ff",
12722       "#ff00ff", "#ff00cc", "#ff0099", "#ff0066", "#ff0033",
12723       "#ff0000", "#ff3300", "#ff6600", "#ff9900", "#ffcc00",
12724       "#ffff00", "#ccff00", "#99ff00", "#66ff00", "#33ff00",
12725       "#00ff00", "#00ff33", "#00ff66", "#00ff99", "#00ffcc",
12726       "#00ffff", "#00ccff", "#0099ff", "#0066ff", "#0033ff"
12727    } ;
12728    int wid , icol ; Widget ws ;
12729 
12730    if( seq->render_scal == NULL ) return ;
12731 
12732    XtManageChild( seq->render_scal ) ;
12733    XtVaSetValues( seq->render_scal , XmNrightAttachment,XmATTACH_FORM , NULL ) ;
12734 
12735    ws = XtNameToWidget(seq->render_scal,"Scrollbar") ;
12736    icol = lrand48() % NCOL ;
12737    MCW_widget_geom( seq->wform , &wid , NULL,NULL,NULL ) ;
12738    if( ws != NULL ){
12739      XtVaSetValues( ws ,
12740                       XtVaTypedArg , XmNtroughColor , XmRString ,
12741                                      cname[icol] , strlen(cname[icol])+1 ,
12742                     NULL ) ;
12743      XWarpPointer( XtDisplay(ws) , None , XtWindow(ws) ,
12744                    0,0,0,0 , wid/2+1 , METER_HEIGHT/4 ) ;
12745    }
12746 
12747    MCW_widget_geom( seq->wform , &wid , NULL,NULL,NULL ) ;
12748    XtVaSetValues( seq->render_scal , XmNwidth , wid , NULL ) ;
12749    XmUpdateDisplay(seq->render_scal) ;
12750    return ;
12751 }
12752 
12753 /*---------------------------------------------------------------------*/
12754 
ISQ_render_scal_CB(Widget w,XtPointer client_data,XtPointer call_data)12755 void ISQ_render_scal_CB( Widget w, XtPointer client_data, XtPointer call_data )
12756 {
12757    MCW_imseq *seq = (MCW_imseq *)client_data ;
12758    XmScaleCallbackStruct *cbs = (XmScaleCallbackStruct *)call_data ;
12759    float fff ;
12760    int ival ;
12761 
12762    if( ! ISQ_VALID(seq) ) return ;
12763 
12764    if( cbs != NULL ) ival = cbs->value ;
12765    else              XmScaleGetValue( w , &ival ) ;
12766 
12767    seq->render_fac = 0.01f * ival ;
12768    ISQ_redisplay( seq , -1 , isqDR_display ) ;
12769    ISQ_draw_winfo( seq ) ;
12770    return ;
12771 }
12772 
12773 /*---------------------------------------------------------------------*/
12774 /*! Deal with dragging a crop window after a button has been pressed.
12775 -----------------------------------------------------------------------*/
12776 
ISQ_cropper(MCW_imseq * seq,XButtonEvent * event)12777 void ISQ_cropper( MCW_imseq *seq , XButtonEvent *event )
12778 {
12779    int x1=event->x,y1=event->y , x2,y2 ;
12780    int imx1,imy1,nim1 , imx2,imy2,nim2 , tt ;
12781    int zlev = seq->zoom_fac ;
12782 
12783 ENTRY("ISQ_cropper") ;
12784 
12785    if( !seq->crop_allowed ){
12786      XBell(seq->dc->display,100); EXRETURN;
12787    }
12788 
12789    /*** make the user drag a rectangle while button is pressed:
12790         (x1,y1) = window coords of rectangle start
12791         (x2,y2) = window coords of rectangle finish         ***/
12792 
12793 #if 1
12794    RWC_drag_rectangle( seq->wimage , x1,y1,&x2,&y2 ) ;
12795 #else
12796    { int rad ;
12797      RWC_drag_circle( seq->wimage , x1,y1 , &rad ) ;  /** just a test **/
12798      fprintf(stderr,"rad=%d\n",rad) ; EXRETURN ;
12799    }
12800 #endif
12801 
12802    /*** find corners of rectangle in original image pixels ***/
12803 
12804    ISQ_mapxy( seq , x1,y1 , &imx1,&imy1,&nim1 ) ;
12805    ISQ_mapxy( seq , x2,y2 , &imx2,&imy2,&nim2 ) ;
12806 
12807    /*** ensure coords of rectangle run upwards (upperleft to lowerright) ***/
12808 
12809    if( imx1 > imx2 ){ tt = imx1; imx1 = imx2; imx2 = tt; }
12810    if( imy1 > imy2 ){ tt = imy1; imy1 = imy2; imy2 = tt; }
12811 
12812    /*** if dragging occured across sub-images in a montage,
12813         or if rectangle edge is in a Montage's inter-image border */
12814 
12815    if( nim1 != nim2 || imx1 < 0 || imy1 < 0 ){
12816      static int npop=0 ;
12817      char str[64] ;
12818      if( npop < 5 ){
12819        sprintf(str,
12820                " \n  %s \n  crop\n  rectangle! \n\n[Crosses montage border]\n",
12821                Random_Insult()) ;
12822        MCW_popup_message( seq->wimage,str, MCW_USER_KILL|MCW_TIMER_KILL ) ;
12823        npop++ ;
12824      }
12825      XBell(seq->dc->display,100); goto CropDone;
12826    }
12827 
12828    /*** if crop window is too small, then deal with that ***/
12829 
12830    if( imx2-imx1 < MINCROP || imy2-imy1 < MINCROP ){ /* too small */
12831      if( imx2-imx1 < 2 || imy2-imy1 < 2 ){
12832        seq->cropit = 0 ; seq->crop_nxorg = seq->crop_nyorg = -1 ;  /* turn crop off */
12833      } else {
12834        XBell(seq->dc->display,100);                 /* do nothing */
12835      }
12836 
12837    /*** otherwise (not too small), set the crop region ***/
12838 
12839    } else {
12840 
12841      /* 14 Jun 2002: if we are also zoomed, things are more complex */
12842 
12843      if( zlev > 1 ){
12844 
12845        /* xmid = middle of crop region */
12846        /* xh   = half-width of crop region, as drawn */
12847        /* xhw  = half-width enlarged by zoom factor */
12848 
12849        int xmid=(imx2+imx1)/2, xh=(imx2-imx1)/2, xhw=zlev*xh ;
12850        int ymid=(imy2+imy1)/2, yh=(imy2-imy1)/2, yhw=zlev*yh ;
12851        int nx,ny ;
12852        float mh = (zlev-1.001f)/zlev ;  /* max offset allowed */
12853 
12854        /* set size of original image from which cropping will be done */
12855 
12856        nx = (seq->crop_nxorg > 0) ? seq->crop_nxorg : seq->horig ;
12857        ny = (seq->crop_nyorg > 0) ? seq->crop_nyorg : seq->vorig ;
12858 #if 0
12859 fprintf(stderr,"Crop: imx1=%d imx2=%d xmid=%d xh=%d xhw=%d nx=%d\n",imx1,imx2,xmid,xh,xhw,nx);
12860 fprintf(stderr,"      imy1=%d imy2=%d ymid=%d yh=%d yhw=%d ny=%d\n",imy1,imy2,ymid,yh,yhw,ny);
12861 #endif
12862 
12863        /* cropping should run from imx1-xhw to imx2+xhw
12864           (since we want the image window to show what the user
12865            drew, so we have to crop a larger rectangle and then
12866            zoom in on THAT),
12867           but we can't go outside the original image boundaries,
12868           so we recompute imx1..imx2 here                       */
12869 
12870        imx1 = xmid-xhw ; imx2 = xmid+xhw ;
12871             if( imx1 <  0    ){ imx1 = 0   ; imx2 = imx1+2*xhw; }
12872        else if( imx2 >= nx-1 ){ imx2 = nx-1; imx1 = imx2-2*xhw; }
12873        imy1 = ymid-yhw ; imy2 = ymid+yhw ;
12874             if( imy1 <  0    ){ imy1 = 0   ; imy2 = imy1+2*yhw; }
12875        else if( imy2 >= ny-1 ){ imy2 = ny-1; imy1 = imy2-2*yhw; }
12876 
12877        /* set the offset for the zoom window so that we'll show
12878           the crop region just computed in the image display    */
12879 
12880        if( seq->opt.mirror )
12881          seq->zoom_hor_off = ((float)(imx2-xmid-xh))
12882                             /((float)(imx2-imx1)) ;
12883        else
12884          seq->zoom_hor_off = ((float)(xmid-xh-imx1))
12885                             /((float)(imx2-imx1)) ;
12886 
12887        seq->zoom_ver_off = ((float)(ymid-yh-imy1))
12888                           /((float)(imy2-imy1)) ;
12889 #if 0
12890 fprintf(stderr,"      imx1=%d imx2=%d hor_off=%f\n",imx1,imx2,seq->zoom_hor_off);
12891 fprintf(stderr,"      imy1=%d imy2=%d ver_off=%f\n",imy1,imy2,seq->zoom_ver_off);
12892 #endif
12893 
12894        /* safeguard: don't let the zoom window offset be out of range! */
12895 
12896             if( seq->zoom_hor_off > mh  ) seq->zoom_hor_off = mh  ;
12897        else if( seq->zoom_hor_off < 0.0 ) seq->zoom_hor_off = 0.0 ;
12898             if( seq->zoom_ver_off > mh  ) seq->zoom_ver_off = mh  ;
12899        else if( seq->zoom_ver_off < 0.0 ) seq->zoom_ver_off = 0.0 ;
12900 
12901      } /* end of mangling crop+zoom interaction */
12902 
12903      /* now set crop parameters */
12904 
12905      seq->crop_xa = imx1 ; seq->crop_xb = imx2 ;
12906      seq->crop_ya = imy1 ; seq->crop_yb = imy2 ;
12907      seq->cropit = 1 ; seq->crop_nxorg = seq->crop_nyorg = -1 ;
12908    }
12909 
12910    /*** force image redisplay ***/
12911 
12912 CropDone:
12913    if( seq->crop_drag ){                       /* turn off crop */
12914      MCW_invert_widget( seq->crop_drag_pb ) ;  /* button, if on */
12915      seq->crop_drag = 0 ;
12916    }
12917 
12918    ISQ_redisplay( seq , -1 , isqDR_display ) ;
12919    EXRETURN ;
12920 }
12921 
12922 /**************************************************************************/
12923 /*** 20 Jun 2003: snapshot stuff for recording the contents of a widget ***/
12924 
12925 /*! Xt warning handler (to avoid messages to screen). */
12926 
SNAP_warnhandler(char * msg)12927 static void SNAP_warnhandler(char *msg){ return ; }
12928 
12929 /*----------------------------------------------------------------------*/
12930 
12931 static MCW_imseq *snap_isq  = NULL ;
12932 static MCW_DC    *snap_dc   = NULL ;  /* cf. SNAP_make_dc() */
12933 static MRI_IMARR *snap_imar = NULL ;
12934 
12935 static void SNAP_imseq_send_CB( MCW_imseq *, XtPointer, ISQ_cbs * ) ;
12936 
12937 /*------------------------------------------------------------------*/
12938 
ISQ_snap_agif(char * prefix)12939 void ISQ_snap_agif( char *prefix )
12940 {
12941    ISQ_save_anim( snap_isq , prefix , 0,0 , AGIF_MODE ) ;
12942 }
ISQ_snap_agif_rng(char * prefix,int a,int b)12943 void ISQ_snap_agif_rng( char *prefix , int a, int b )
12944 {
12945    ISQ_save_anim( snap_isq , prefix , a,b , AGIF_MODE ) ;
12946 }
12947 
12948 /*------------------------------------------------------------------*/
12949 
ISQ_snap_mpeg(char * prefix)12950 void ISQ_snap_mpeg( char *prefix )
12951 {
12952    ISQ_save_anim( snap_isq , prefix , 0,0 , MPEG_MODE ) ;
12953 }
ISQ_snap_mpeg_rng(char * prefix,int a,int b)12954 void ISQ_snap_mpeg_rng( char *prefix , int a, int b )
12955 {
12956    ISQ_save_anim( snap_isq , prefix , a,b , MPEG_MODE ) ;
12957 }
12958 
12959 /*------------------------------------------------------------------*/
12960 
ISQ_snap_jpeg(char * prefix)12961 void ISQ_snap_jpeg( char *prefix )
12962 {
12963    ISQ_save_anim( snap_isq , prefix , 0,0 , JPEG_MODE ) ;
12964 }
ISQ_snap_jpeg_rng(char * prefix,int a,int b)12965 void ISQ_snap_jpeg_rng( char *prefix , int a, int b )
12966 {
12967    ISQ_save_anim( snap_isq , prefix , a,b , JPEG_MODE ) ;
12968 }
12969 
12970 /*------------------------------------------------------------------*/
12971 
ISQ_snap_png(char * prefix)12972 void ISQ_snap_png( char *prefix )
12973 {
12974    ISQ_save_anim( snap_isq , prefix , 0,0 , PNG_MODE ) ;
12975 }
ISQ_snap_png_rng(char * prefix,int a,int b)12976 void ISQ_snap_png_rng( char *prefix , int a, int b )
12977 {
12978    ISQ_save_anim( snap_isq , prefix , a,b , PNG_MODE ) ;
12979 }
12980 
12981 /*------------------------------------------------------------------
12982    Routine to provide data to the imseq.
12983    Just returns the control information, or the selected image.
12984 --------------------------------------------------------------------*/
12985 
SNAP_imseq_getim(int n,int type,XtPointer handle)12986 static XtPointer SNAP_imseq_getim( int n, int type, XtPointer handle )
12987 {
12988    int ntot = 0 ;
12989 
12990 ENTRY("SNAP_imseq_getim") ;
12991 
12992    if( snap_imar != NULL ) ntot = IMARR_COUNT(snap_imar) ;
12993    if( ntot < 1 ) ntot = 1 ;
12994 
12995    /*--- send control info ---*/
12996 
12997    if( type == isqCR_getstatus ){
12998      MCW_imseq_status *stat = myXtNew( MCW_imseq_status ) ; /* will be freed */
12999                                                             /* when imseq is */
13000                                                             /* destroyed    */
13001      stat->num_total  = ntot ;
13002      stat->num_series = ntot ;
13003      stat->send_CB    = SNAP_imseq_send_CB ;
13004      stat->parent     = NULL ;
13005      stat->aux        = NULL ;
13006 
13007      stat->transforms0D = NULL ;
13008      stat->transforms2D = NULL ;
13009      stat->slice_proj   = NULL ;
13010 
13011      RETURN( (XtPointer)stat ) ;
13012    }
13013 
13014    /*--- return a copy of an image
13015          (since the imseq will delete it when it is done) ---*/
13016 
13017    if( type == isqCR_getimage || type == isqCR_getqimage ){
13018      MRI_IMAGE *im = NULL , *rim ;
13019 
13020      if( snap_imar != NULL ){
13021        if( n < 0 ) n = 0 ; else if( n >= ntot ) n = ntot-1 ;
13022        rim = IMARR_SUBIMAGE(snap_imar,n) ;
13023        im  = mri_copy( rim ) ;
13024      }
13025      RETURN( (XtPointer)im );
13026    }
13027 
13028    RETURN( NULL ) ; /* all other cases */
13029 }
13030 
13031 /*---------------------------------------------------------------------------
13032    Routine called when the imseq wants to send a message.
13033    In this case, all we need to handle is the destroy message,
13034    so that we can free some memory.
13035 -----------------------------------------------------------------------------*/
13036 
SNAP_imseq_send_CB(MCW_imseq * seq,XtPointer handle,ISQ_cbs * cbs)13037 static void SNAP_imseq_send_CB( MCW_imseq *seq, XtPointer handle, ISQ_cbs *cbs )
13038 {
13039 ENTRY("SNAP_imseq_send_CB") ;
13040    switch( cbs->reason ){
13041      case isqCR_destroy:{
13042        myXtFree(snap_isq) ;         snap_isq  = NULL ;
13043        DESTROY_IMARR( snap_imar ) ; snap_imar = NULL ;
13044      }
13045      break ;
13046    }
13047    EXRETURN ;
13048 }
13049 /*------------------------------------------------------------------------*/
13050 /*! Create display context if we don't have one.  [03 Jul 2003] */
13051 
SNAP_make_dc(Widget w)13052 static void SNAP_make_dc( Widget w )
13053 {
13054 ENTRY("SNAP_make_dc") ;
13055    if( snap_dc == NULL ){
13056      if( first_dc != NULL ) snap_dc = first_dc ;
13057      else{
13058        if( w == (Widget) NULL ){
13059          fprintf(stderr,"** Can't snapshot/save with NULL widget!\n") ;
13060          EXRETURN ;
13061        }
13062        (void ) XtAppSetWarningHandler( XtWidgetToApplicationContext(w),
13063                                        SNAP_warnhandler ) ;
13064        snap_dc = MCW_new_DC( w, 4,0, NULL,NULL, 1.0,0 ) ;
13065      }
13066    }
13067    EXRETURN ;
13068 }
13069 
13070 static int NoDuplicates = 1;
SNAP_NoDuplicates(void)13071 void SNAP_NoDuplicates (void) { NoDuplicates = 1; return; }
SNAP_OkDuplicates(void)13072 void SNAP_OkDuplicates (void) { NoDuplicates = 0; return; }
13073 
13074 /*-------------------------------------------------------------------------*/
13075 /*! Save image into a viewer, which should be opened near the widget w. */
13076 
SNAP_store_image(MRI_IMAGE * tim,Widget w)13077 static void SNAP_store_image( MRI_IMAGE *tim , Widget w )
13078 {
13079 ENTRY("SNAP_store_image") ;
13080 
13081    if( tim == NULL ) EXRETURN ;
13082 
13083    if( snap_imar == NULL ) INIT_IMARR(snap_imar) ;
13084 
13085    if( NoDuplicates && IMARR_COUNT(snap_imar) > 0 ){
13086      MRI_IMAGE *qim = IMARR_LASTIM( snap_imar ) ;
13087      if( mri_equal(qim,tim) ){
13088        fprintf(stderr,"++ Image recorder: reject duplicate image at #%d\n",
13089                IMARR_COUNT(snap_imar)-1 ) ;
13090        mri_free(tim); EXRETURN;
13091      }
13092    }
13093 
13094    ADDTO_IMARR(snap_imar,tim) ;
13095 
13096    /* create viewer, if not present already */
13097 
13098    if( snap_isq == NULL ){
13099      int xr,yr , wx,hy , xx,yy ;
13100      Position xroot,yroot ;
13101      Widget wpar ;
13102 
13103      SNAP_make_dc( w ) ; if( snap_dc == NULL ) EXRETURN ;
13104 
13105      snap_isq = open_MCW_imseq( snap_dc, SNAP_imseq_getim, NULL ) ;
13106 
13107      drive_MCW_imseq( snap_isq, isqDR_periodicmont, (XtPointer) 0 ) ;
13108      drive_MCW_imseq( snap_isq, isqDR_realize     , NULL          ) ;
13109      drive_MCW_imseq( snap_isq, isqDR_title       , "Snapshots"   ) ;
13110 
13111      /* put next to top shell of widget we are snapshotting */
13112 
13113      if( w != (Widget) NULL ){
13114        wpar = w ;
13115        while( XtParent(wpar) != NULL ) wpar = XtParent(wpar) ;  /* find top */
13116        XtTranslateCoords( wpar , 0,0 , &xroot,&yroot ) ;
13117        xr = (int) xroot ; yr = (int) yroot ;
13118        MCW_widget_geom( wpar , &wx,NULL , NULL,NULL ) ;
13119        xx = 1+wx+xr ; yy = 1+yr ;
13120        if( xx >= snap_dc->width-wx/3 ){
13121          XLowerWindow( snap_dc->display , XtWindow(wpar) ) ; xx = yy = 2 ;
13122        }
13123        XtVaSetValues( snap_isq->wtop , XmNx,xx , XmNy,yy , NULL ) ;
13124      }
13125    }
13126 
13127    /* tell the image viewer about the new image */
13128 
13129    if( IMARR_COUNT(snap_imar) > 1 ){
13130      int ii ;
13131      drive_MCW_imseq( snap_isq, isqDR_newseq      , NULL ) ;
13132      drive_MCW_imseq( snap_isq, isqDR_onoffwid    , (XtPointer)isqDR_onwid  );
13133 
13134      /* turn off some controls that don't make sense here */
13135 
13136      XtUnmanageChild( snap_isq->wbar ) ;
13137      XtUnmanageChild( snap_isq->arrowpad->wform ) ;
13138      for( ii=0 ; ii < NBUTTON_RIG ; ii++)
13139        XtUnmanageChild( snap_isq->wbut_rig[ii] ) ;
13140      for( ii=0 ; ii < NARROW-1 ; ii++ ) /* keep "i" arrow */
13141        XtUnmanageChild( snap_isq->arrow[ii]->wrowcol ) ;
13142      XtUnmanageChild( snap_isq->ov_opacity_sep ) ;
13143      XtUnmanageChild( snap_isq->ov_opacity_av->wrowcol ) ;
13144      XtUnmanageChild( snap_isq->winfo ) ;
13145      XtUnmanageChild( snap_isq->pen_bbox->wrowcol ) ;
13146 
13147    } else {
13148      drive_MCW_imseq( snap_isq, isqDR_onoffwid    , (XtPointer)isqDR_offwid );
13149    }
13150 
13151    /* force display of the new image */
13152 
13153    ISQ_redisplay( snap_isq , IMARR_COUNT(snap_imar)-1 , isqDR_display ) ;
13154 
13155    EXRETURN ;
13156 }
13157 
13158 /*----------------------------------------------------------------------*/
13159 /*! Call this function to get a snapshot of a widget and save
13160     it into an image viewer.  Also see ISQ_snapsave().
13161 ------------------------------------------------------------------------*/
13162 
ISQ_snapshot(Widget w)13163 void ISQ_snapshot( Widget w )
13164 {
13165    MRI_IMAGE *tim ;
13166    Window win ;
13167 
13168 ENTRY("ISQ_snapshot") ;
13169 
13170    if( w == NULL || !XtIsWidget(w) )         EXRETURN ;
13171    if( !XtIsRealized(w) || !XtIsManaged(w) ) EXRETURN ;
13172    win = XtWindow(w); if( win == (Window)0 ) EXRETURN ;
13173 
13174    /* try to get image */
13175 
13176    SNAP_make_dc( w ) ; if( snap_dc == NULL ) EXRETURN ;
13177 
13178    tim = SNAP_grab_image( w , snap_dc ) ;
13179    if( tim == NULL )                         EXRETURN ;
13180 
13181    /* got image; save it and display it */
13182 
13183    SNAP_store_image( tim , w ) ;
13184    EXRETURN ;
13185 }
13186 
13187 /*----------------------------------------------------------------------------*/
13188 /*! Called to add an image directly to the snapshot save sequence.
13189      - ww, hh = width and height of image
13190      - if(hh < 0) ==> flip image vertically (e.g., from glReadPixels)
13191      - pix = pointer to 3*ww*hh bytes of RGB data
13192      - w = Widget that the view should popup next to (can't be NULL)
13193      - RWCox - 03 Jul 2003
13194 ------------------------------------------------------------------------------*/
13195 
ISQ_snapsave(int ww,int hh,byte * pix,Widget w)13196 void ISQ_snapsave( int ww , int hh , byte *pix , Widget w )
13197 {
13198    MRI_IMAGE *tim ;
13199    byte *qix ;
13200    int ii , jj , flip=0 ;
13201 
13202 ENTRY("ISQ_snapsave") ;
13203 
13204    if( ww < 2 || pix == NULL ) EXRETURN ;
13205    if( hh < 0 ){ hh = -hh ; flip = 1 ; }
13206    if( hh < 2 ) EXRETURN ;
13207 
13208    SNAP_make_dc( w ) ; if( snap_dc == NULL ) EXRETURN ;
13209 
13210    tim = mri_new( ww,hh, MRI_rgb ) ; qix = MRI_RGB_PTR(tim) ;
13211 
13212    if( flip ){                    /* flipper, flipper, faster than lightning */
13213      for( jj=0 ; jj < hh ; jj++ )
13214        memcpy( qix+3*ww*(hh-jj-1) , pix+3*ww*jj , 3*ww ) ;
13215    } else {                                                   /* simple copy */
13216      memcpy( qix , pix , 3*ww*hh ) ;
13217    }
13218 
13219    SNAP_store_image( tim , w ) ;
13220    EXRETURN ;
13221 }
13222 
13223 /*----------------------------------------------------------------------------*/
13224 /*! Like ISQ_snapsave, but don't store the image, return it instead.
13225      - ww, hh = width and height of image
13226      - if(hh < 0) ==> flip image vertically (e.g., from glReadPixels)
13227      - pix = pointer to 3*ww*hh bytes of RGB data
13228 ------------------------------------------------------------------------------*/
13229 
ISQ_snap_to_mri_image(int ww,int hh,byte * pix)13230 MRI_IMAGE * ISQ_snap_to_mri_image( int ww , int hh , byte *pix  )
13231 {
13232    MRI_IMAGE *tim ;
13233    byte *qix ;
13234    int ii , jj , flip=0 ;
13235 
13236    ENTRY("ISQ_snap_to_mri_image") ;
13237 
13238    if( ww < 2 || pix == NULL ) RETURN(NULL) ;
13239    if( hh < 0 ){ hh = -hh ; flip = 1 ; }
13240    if( hh < 2 )                RETURN(NULL) ;
13241 
13242    tim = mri_new( ww,hh, MRI_rgb ) ; qix = MRI_RGB_PTR(tim) ;
13243 
13244    if( flip ){                    /* flipper, flipper, faster than lightning */
13245      for( jj=0 ; jj < hh ; jj++ )
13246        memcpy( qix+3*ww*(hh-jj-1) , pix+3*ww*jj , 3*ww ) ;
13247    } else {                                                   /* simple copy */
13248      memcpy( qix , pix , 3*ww*hh ) ;
13249    }
13250 
13251    RETURN(tim) ;
13252 }
13253 
13254 /*----------------------------------------------------------------------------*/
13255 /*! Like ISQ_snap_to_mri_image, but pix is a pointer to RGBA data.
13256       Returned image is still MRI_rbg, so alphas are ignored.
13257 ------------------------------------------------------------------------------*/
13258 
ISQ_snap4_to_mri_image(int ww,int hh,byte * pix)13259 MRI_IMAGE * ISQ_snap4_to_mri_image( int ww , int hh , byte *pix  )
13260 {
13261    MRI_IMAGE *tim ;
13262    byte *qix ;
13263    int ii , jj , flip=0, nn3, nn4 ;
13264 
13265    ENTRY("ISQ_snap4_to_mri_image") ;
13266 
13267    if( ww < 2 || pix == NULL ) RETURN(NULL) ;
13268    if( hh < 0 ){ hh = -hh ; flip = 1 ; }
13269    if( hh < 2 )                RETURN(NULL) ;
13270 
13271    tim = mri_new( ww,hh, MRI_rgb ) ; qix = MRI_RGB_PTR(tim) ;
13272 
13273    if( flip ){                    /* flip vertically */
13274      for (jj=(hh-1), nn3=0; jj>=0; --jj) {
13275      for (ii=0; ii < ww; ii++ ) {
13276          nn4 = (jj*ww+ii)*4;
13277          qix[nn3++] = pix[nn4];
13278          qix[nn3++] = pix[nn4+1];
13279          qix[nn3++] = pix[nn4+2];
13280      } }
13281    } else {                                                   /* simple copy */
13282      for (jj=0, nn3=0, nn4=0; jj < hh; jj++ ) {
13283      for (ii=0; ii < ww; ii++ ) {
13284          qix[nn3++] = pix[nn4++];
13285          qix[nn3++] = pix[nn4++];
13286          qix[nn3++] = pix[nn4++]; ++nn4;
13287      }}
13288    }
13289 
13290    RETURN(tim) ;
13291 }
13292 
13293 /*----------------------------------------------------------------------------*/
13294 
ISQ_pen_bbox_CB(Widget w,XtPointer client_data,XtPointer call_data)13295 void ISQ_pen_bbox_CB( Widget w, XtPointer client_data, XtPointer call_data )
13296 {
13297    MCW_imseq *seq = (MCW_imseq *)client_data ;
13298    int val ;
13299 
13300 ENTRY("ISQ_pen_bbox_CB") ;
13301    if( !ISQ_REALZ(seq) ) EXRETURN ;                 /* bad, but impossible */
13302 
13303    if( !seq->button2_enabled ){                     /* shouldn't happen */
13304      MCW_set_bbox( seq->pen_bbox , 0 ) ;
13305      ISQ_set_cursor_state( seq, CURSOR_NORMAL ) ;
13306      XtUnmanageChild( seq->pen_bbox->wrowcol ) ;
13307      EXRETURN ;
13308    }
13309 
13310    val = MCW_val_bbox( seq->pen_bbox ) ;
13311    ISQ_set_cursor_state( seq, (val==0) ? CURSOR_NORMAL : CURSOR_PENCIL ) ;
13312    EXRETURN ;
13313 }
13314 
13315 /*----------------------------------------------------------------------------*/
13316 /*! Do something every so often. */
13317 
ISQ_timer_CB(XtPointer cd,XtIntervalId * id)13318 void ISQ_timer_CB( XtPointer cd , XtIntervalId *id ) /* 03 Dec 2003 */
13319 {
13320    MCW_imseq *seq = (MCW_imseq *)cd ;
13321    int redo = 0 ;
13322 
13323 ENTRY("ISQ_timer_CB") ;
13324 
13325    if( !ISQ_REALZ(seq) || seq->timer_id == 0 ) EXRETURN ;
13326 
13327    switch( seq->timer_func ){
13328 
13329      case ISQ_TIMERFUNC_INDEX:{
13330        int nn=seq->im_nr , nt=seq->status->num_total ;
13331        if( nt > 1 && seq->timer_param != 0 ){
13332          nn = (nn+seq->timer_param+nt) % nt ;
13333          ISQ_redisplay( seq , nn , isqDR_display ) ;
13334          redo = 1 ;
13335        }
13336      }
13337      break ;
13338 
13339      case ISQ_TIMERFUNC_BOUNCE:{
13340        int nn=seq->im_nr , nt=seq->status->num_total ;
13341        if( nt > 1 && seq->timer_param != 0 ){
13342          nn = nn + seq->timer_param ;
13343          if( nn <  0  ){
13344            nn = -nn; seq->timer_param = -seq->timer_param;
13345          } else if( nn >= nt ){
13346            nn = 2*(nt-1)-nn; seq->timer_param = -seq->timer_param;
13347          }
13348          ISQ_redisplay( seq , nn , isqDR_display ) ;
13349          redo = 1 ;
13350        }
13351      }
13352      break ;
13353 
13354    }
13355 
13356    if( redo ) seq->timer_id = XtAppAddTimeOut(
13357                                XtWidgetToApplicationContext(seq->wform) ,
13358                                seq->timer_delay , ISQ_timer_CB , seq ) ;
13359    else       seq->timer_id = 0 ;
13360 
13361    EXRETURN ;
13362 }
13363 
ISQ_timer_stop(MCW_imseq * seq)13364 void ISQ_timer_stop( MCW_imseq *seq )
13365 {
13366 ENTRY("ISQ_timer_stop") ;
13367    if( seq != NULL && seq->timer_id > 0 ){
13368      XtRemoveTimeOut(seq->timer_id); seq->timer_id = 0;
13369    }
13370    EXRETURN ;
13371 }
13372 
13373 /*--------------------------------------------------------------------*/
13374 /*! Deal with a single keypress in an image viewer window.
13375     Return value is 1 if processed OK, 0 if not.
13376 ----------------------------------------------------------------------*/
13377 
ISQ_handle_keypress(MCW_imseq * seq,unsigned long key,unsigned int state)13378 int ISQ_handle_keypress( MCW_imseq *seq , unsigned long key , unsigned int state )
13379 {
13380    static int busy=0 ;   /* prevent recursion */
13381 
13382    int shft = (state & ShiftMask) ;    /* 25 Aug 2009: stuff for  */
13383    int ctrl = (state & ControlMask) ;  /* editing crop window via */
13384    int astp ;                          /* arrow keypresses        */
13385 
13386 ENTRY("ISQ_handle_keypress") ;
13387 
13388    ISQ_timer_stop(seq) ;  /* 03 Dec 2003 */
13389 
13390    if( busy || key == 0 ) RETURN(1) ;
13391    busy = 1 ;
13392 
13393    astp = (int)AFNI_numenv("AFNI_IMAGE_CROPSTEP") ;
13394         if( astp == 0 ) astp =  1 ;  /* default */
13395    else if( astp >  9 ) astp =  9 ;  /* maximum */
13396    else if( astp < -9 ) astp = -9 ;  /* minimum */
13397 
13398    /* 24 Jan 2003: deal with special function keys */
13399 
13400    if( key > 255 ){
13401      KeySym ks = (KeySym)key ;
13402     switch( ks ){
13403 
13404        case XK_Home:       /* 27 Aug 2009 : center crop or pan at crosshairs */
13405          if( shft ){
13406            ISQ_adjust_crop( seq, 0,0,0,0 , 1 ) ;  /* crop center */
13407          } else if (ctrl ){
13408            /* nada */
13409          } else {
13410            ISQ_center_zoom( seq ) ;           /* pan center */
13411          }
13412        break ;
13413 
13414        case XK_Left:
13415        case XK_KP_Left:
13416          if( shft ){                        /* 25 Aug 2009: edit crop window */
13417            ISQ_adjust_crop( seq , +astp,+astp , 0,0 , 1 ) ;
13418          } else if( ctrl ){
13419            ISQ_adjust_crop( seq , +1,-1 , 0,0 , 1 ) ;
13420          } else {
13421            seq->arrowpad->which_pressed = AP_LEFT ;
13422            seq->arrowpad->xev.type = 0 ;
13423            ISQ_arrowpad_CB( seq->arrowpad , (XtPointer)seq ) ;
13424          }
13425        break ;
13426 
13427        case XK_Right:
13428        case XK_KP_Right:
13429          if( shft ){
13430            ISQ_adjust_crop( seq , -astp,-astp , 0,0 , 1 ) ;
13431          } else if( ctrl ){
13432            ISQ_adjust_crop( seq , -1,+1 , 0,0 , 1 ) ;
13433          } else {
13434            seq->arrowpad->which_pressed = AP_RIGHT ;
13435            seq->arrowpad->xev.type = 0 ;
13436            ISQ_arrowpad_CB( seq->arrowpad , (XtPointer)seq ) ;
13437          }
13438        break ;
13439 
13440        case XK_Down:
13441        case XK_KP_Down:
13442          if( shft ){
13443            ISQ_adjust_crop( seq , 0,0 , -astp,-astp , 1 ) ;
13444          } else if( ctrl ){
13445            ISQ_adjust_crop( seq , 0,0 , -1,+1 , 1 ) ;
13446          } else {
13447            seq->arrowpad->which_pressed = AP_DOWN ;
13448            seq->arrowpad->xev.type = 0 ;
13449            ISQ_arrowpad_CB( seq->arrowpad , (XtPointer)seq ) ;
13450          }
13451        break ;
13452 
13453        case XK_Up:
13454        case XK_KP_Up:
13455          if( shft ){
13456            ISQ_adjust_crop( seq , 0,0 , +astp,+astp , 1 ) ;
13457          } else if( ctrl ){
13458            ISQ_adjust_crop( seq , 0,0 , +1,-1 , 1 ) ;
13459          } else {
13460            seq->arrowpad->which_pressed = AP_UP ;
13461            seq->arrowpad->xev.type = 0 ;
13462            ISQ_arrowpad_CB( seq->arrowpad , (XtPointer)seq ) ;
13463          }
13464        break ;
13465 
13466        case XK_Page_Up:
13467        case XK_KP_Page_Up:
13468        case XK_Page_Down:
13469        case XK_KP_Page_Down:{
13470          int nn=seq->im_nr , nt=seq->status->num_total ;
13471          if( nt > 1 ){
13472            if( ks==XK_Page_Down || ks==XK_KP_Page_Down ){ nn--; if(nn< 0 ) nn=nt-1; }
13473            else                                         { nn++; if(nn>=nt) nn=0   ; }
13474 #if 1
13475            ISQ_redisplay( seq , nn , isqDR_display ) ;
13476 #else
13477            ISQ_set_image_number( seq , nn ) ;
13478 #endif
13479          }
13480        }
13481        break ;
13482 
13483        case XK_Delete:              /* 20 Feb 2003: drawing undo */
13484        case XK_KP_Delete:
13485          if( seq->button2_enabled && seq->status->send_CB != NULL ){
13486            ISQ_cbs cbs ;
13487            cbs.reason   = isqCR_button2_key ;
13488            cbs.key      = (int) XK_Delete ;
13489 #if 0
13490            seq->status->send_CB( seq , seq->getaux , &cbs ) ;
13491 #else
13492            SEND(seq,cbs) ;
13493 #endif
13494          }
13495        break ;
13496 
13497        /* 10 Mar 2003: change cursor state to drawing pencil */
13498 
13499        case XK_F2:{
13500          if( !seq->button2_enabled ){
13501            MCW_popup_message( seq->wimage,
13502                               " \n Only when \n"
13503                               " Drawing!! \n ", MCW_USER_KILL|MCW_TIMER_KILL );
13504            XBell(seq->dc->display,100); busy=0; RETURN(0);
13505          }
13506          ISQ_set_cursor_state( seq ,
13507                                (seq->cursor_state == CURSOR_PENCIL)
13508                                ? CURSOR_NORMAL : CURSOR_PENCIL ) ;
13509        }
13510        break ;
13511 
13512        case XK_F4:
13513        case XK_F3:{                     /* 13 Sep 2008 */
13514          ISQ_cbs cbs ;
13515          if( !seq->button2_enabled ){
13516            MCW_popup_message( seq->wimage,
13517                               " \n Only when \n"
13518                               " Drawing!! \n ", MCW_USER_KILL|MCW_TIMER_KILL );
13519            XBell(seq->dc->display,100); busy=0; RETURN(0);
13520          }
13521          cbs.reason = isqCR_button2_key ;
13522          cbs.key    = (int)ks ;
13523          SEND(seq,cbs) ;
13524        }
13525        break ;
13526 
13527 #if 0
13528        case XK_F5:
13529          MCW_melt_widget( seq->wform ) ;
13530        break ;
13531 #endif
13532 
13533        default:
13534        /* case XK_F5: */
13535        case XK_F6:
13536        case XK_F7:
13537        case XK_F8:
13538        case XK_F9:
13539        case XK_F10:
13540        case XK_F11:
13541        case XK_F12:
13542 #if 0
13543          XBell(seq->dc->display,100) ;
13544          MCW_popup_message( seq->wimage, " \n Ouch! \n ", MCW_USER_KILL|MCW_QUICK_KILL );
13545          AFNI_speak( "Ouch!" , 0 ) ;
13546 #endif
13547        break ;
13548      }
13549      busy=0; RETURN(1) ;
13550    }
13551 
13552          /* 07 Dec 2002: modified ad hoc series of if-s into a switch */
13553 
13554    switch( key ){
13555 
13556      /* 10 Mar 2002: quit if 'q' or 'Q' is pressed */
13557 
13558      case 'q':
13559      case 'Q':{
13560        ISQ_but_done_CB( NULL, (XtPointer)seq, NULL ) ; NI_sleep(1) ;
13561        busy=0; RETURN(1) ;
13562      }
13563      break ;
13564 
13565      /* 03 Dec 2003: advance picture continuously? */
13566 
13567      case 'v':
13568      case 'V':{
13569        if( seq->button2_enabled ){
13570          MCW_popup_message( seq->wimage,
13571                                " \n Not when \n"
13572                                " Drawing! \n ", MCW_USER_KILL|MCW_TIMER_KILL );
13573          XBell(seq->dc->display,100) ;
13574        } else if( seq->status->num_total > 1 ){      /* bring it on */
13575          seq->timer_func  = ISQ_TIMERFUNC_INDEX ;
13576          seq->timer_delay = (int) AFNI_numenv("AFNI_VIDEO_DELAY") ;
13577          if( seq->timer_delay <= 0 ) seq->timer_delay = 1 ;
13578          seq->timer_param = (key == 'v') ? 1 : -1 ;
13579          seq->timer_id    =
13580            XtAppAddTimeOut( XtWidgetToApplicationContext(seq->wform) ,
13581                             seq->timer_delay , ISQ_timer_CB , seq ) ;
13582        }
13583        busy=0; RETURN(1) ;
13584      }
13585      break ;
13586 
13587      case 'r':
13588      case 'R':{
13589        if( seq->button2_enabled ){
13590          MCW_popup_message( seq->wimage,
13591                               " \n Not when \n"
13592                               " Drawing! \n ", MCW_USER_KILL|MCW_TIMER_KILL );
13593          XBell(seq->dc->display,100) ;
13594        } else if( seq->status->num_total > 1 ){      /* bring it on */
13595          seq->timer_func  = ISQ_TIMERFUNC_BOUNCE ;
13596          seq->timer_delay = (int) AFNI_numenv("AFNI_VIDEO_DELAY") ;
13597          if( seq->timer_delay <= 0 ) seq->timer_delay = 1 ;
13598          seq->timer_param = (key == 'r') ? 1 : -1 ;
13599          seq->timer_id    =
13600            XtAppAddTimeOut( XtWidgetToApplicationContext(seq->wform) ,
13601                             seq->timer_delay , ISQ_timer_CB , seq ) ;
13602        }
13603        busy=0; RETURN(1);
13604      }
13605      break ;
13606 
13607      /* 07 Dec 2002: scroll forward or backward
13608                      using '<' or '>' keys (like graphs) */
13609 
13610      case '>':
13611      case '<':
13612      case ',':
13613      case '.':{
13614        int nn=seq->im_nr , nt=seq->status->num_total ;
13615        if( nt > 1 ){
13616          if( key == '<' || key == ',' ){ nn--; if( nn <  0 ) nn = nt-1; }
13617          else                          { nn++; if( nn >= nt) nn = 0   ; }
13618 #if 1
13619          ISQ_redisplay( seq , nn , isqDR_display ) ;
13620 #else
13621          ISQ_set_image_number( seq , nn ) ;
13622 #endif
13623        }
13624        busy=0; RETURN(1) ;
13625      }
13626      break ;
13627 
13628      /* 05 Apr 2002: zoom out/in for 'z' or 'Z' */
13629 
13630      case 'z':
13631      case 'Z':{
13632        int call=0 , zlev=seq->zoom_fac ;
13633        if( key == 'z' && zlev > ZOOM_BOT ){
13634          AV_assign_ival( seq->zoom_val_av , zlev-1 ) ; call = 1 ;
13635        } else if( key == 'Z' && zlev < ZOOM_TOP ){
13636          AV_assign_ival( seq->zoom_val_av , zlev+1 ) ; call = 1 ;
13637        }
13638        if( call )
13639          ISQ_zoom_av_CB( seq->zoom_val_av , (XtPointer)seq ) ;
13640        else
13641          XBell(seq->dc->display,100) ;
13642        busy=0; RETURN(1) ;
13643      }
13644      break ;
13645 
13646      /* and toggle panning with 'p' or 'P' */
13647 
13648      case 'P':
13649      case 'p':{
13650        if( seq->zoom_fac > 1 )
13651          ISQ_zoom_pb_CB( seq->zoom_drag_pb , (XtPointer)seq , NULL ) ;
13652        else
13653          XBell(seq->dc->display,100) ;
13654        busy=0; RETURN(1) ;
13655      }
13656      break ;
13657 
13658      /* 17 Jun 2002: toggle cropping with 'c' or 'C' */
13659 
13660      case 'c':
13661      case 'C':{
13662        ISQ_crop_pb_CB( seq->crop_drag_pb , (XtPointer)seq , NULL ) ;
13663        busy=0; RETURN(1) ;
13664      }
13665      break ;
13666 
13667      /* 17 May 2002: do image fraction up or down */
13668 
13669      case 'i':
13670      case 'I':{
13671        int iv = seq->arrow[NARR_FRAC]->ival ;
13672        if( key == 'i' )
13673          AV_assign_ival( seq->arrow[NARR_FRAC] , iv-1 ) ;
13674        else if( key == 'I' )
13675          AV_assign_ival( seq->arrow[NARR_FRAC] , iv+1 ) ;
13676        ISQ_arrow_CB( seq->arrow[NARR_FRAC] , seq ) ;
13677        busy=0; RETURN(1) ;
13678      }
13679      break ;
13680 
13681     /* ctrl-m to cycle globalranges */
13682      case 13 : {
13683        ISQ_cbs cbs ;
13684        cbs.reason = isqCR_globalrange ;
13685        SEND(seq,cbs) ;   /* send this back to a callback function now in afni.c
13686                             imseq doesn't have access directly to dataset info */
13687 
13688        busy=0 ; RETURN(1) ;
13689      }
13690      break;
13691 
13692      /* 22 Aug 2005: 'm' == Min-to-Max toggle */
13693      case 'm':{
13694        if( seq->dialog_starter==NBUT_DISP ){XBell(seq->dc->display,100); break;}
13695           switch( seq->opt.scale_range ){
13696             default:
13697             case ISQ_RNG_MINTOMAX: seq->opt.scale_range = ISQ_RNG_02TO98;  break;
13698             case ISQ_RNG_CLIPPED:  seq->opt.scale_range = ISQ_RNG_MINTOMAX;break;
13699             case ISQ_RNG_02TO98:   seq->opt.scale_range = ISQ_RNG_MINTOMAX;break;
13700           }
13701        }
13702        ISQ_redisplay( seq , -1 , isqDR_display ) ;
13703        busy=0 ; RETURN(1) ;
13704 
13705      break ;
13706 
13707      /* 22 Aug 2005: 'l' == LR mirror toggle */
13708 
13709      case 'l':{
13710        if( seq->dialog_starter==NBUT_DISP ){XBell(seq->dc->display,100); break;}
13711        seq->opt.mirror = ! seq->opt.mirror ;
13712        ISQ_redisplay( seq , -1 , isqDR_display ) ;
13713        busy=0 ; RETURN(1) ;
13714      }
13715      break ;
13716 
13717      /* 22 Aug 2005: 'a' = fix aspect ratio */
13718 
13719      case 'A':
13720      case 'a':{
13721        int bx = seq->opt.free_aspect ; seq->opt.free_aspect = 0 ;
13722        ISQ_reset_dimen( seq, seq->last_width_mm, seq->last_height_mm ) ;
13723        seq->opt.free_aspect = bx ;
13724        busy=0 ; RETURN(1) ;
13725      }
13726      break ;
13727 
13728      /* 23 Aug 2005: 's' = sharpen */
13729 
13730      case 's':{
13731        if( seq->dialog_starter==NBUT_DISP ){XBell(seq->dc->display,100); break;}
13732        if( !(seq->opt.improc_code & ISQ_IMPROC_SHARP) ){
13733          seq->opt.improc_code |= ISQ_IMPROC_SHARP ;
13734        } else {
13735          int ss = (int)(10.01*seq->sharp_fac)+1 ;
13736          if( ss > 9 ) ss = 1 ;
13737          seq->sharp_fac = 0.1 * ss ;
13738        }
13739        ISQ_redisplay( seq , -1 , isqDR_display ) ;
13740        busy=0 ; RETURN(1) ;
13741      }
13742      break ;
13743 
13744      /* 24 Apr 2014: 'e' = edge detect toggle */
13745 
13746      case 'e':{
13747        if( seq->dialog_starter==NBUT_DISP ){XBell(seq->dc->display,100); break;}
13748        if( !(seq->opt.improc_code & ISQ_IMPROC_SOBEL) ){
13749          seq->opt.improc_code |= ISQ_IMPROC_SOBEL ;   /* turn on edge detection */
13750        } else {
13751          seq->opt.improc_code &= !ISQ_IMPROC_SOBEL ;  /* turn off edge detection */
13752        }
13753        ISQ_redisplay( seq , -1 , isqDR_display ) ;
13754        busy=0 ; RETURN(1) ;
13755      }
13756      break ;
13757 
13758      /* 26 Apr 2007: time indexing */
13759 
13760      case '[':
13761      case ']':{
13762        ISQ_cbs cbs ;
13763        cbs.reason = isqCR_setindex ;
13764        cbs.key    = (key == '[') ? -1 : +1 ;
13765        SEND(seq,cbs) ;
13766      }
13767      break ;  /** N.B.: '{' and '}' are reserved for threshold changing! **/
13768 
13769      /* 23 Aug 2005: open some windows */
13770 
13771      case 'D':
13772        ISQ_but_disp_CB( seq->wbut_bot[NBUT_DISP] , seq , NULL ) ;
13773        busy=0 ; RETURN(1) ;
13774      break ;
13775 
13776      case 'M':
13777        if( seq->status->num_total > 1 )
13778          ISQ_montage_CB( seq->wbut_bot[NBUT_MONT] , seq , NULL ) ;
13779        busy=0 ; RETURN(1) ;
13780      break ;
13781 
13782      case 'S':
13783        if( (seq->opt.save_one || seq->status->num_total > 1) )
13784          ISQ_but_save_CB( seq->wbut_bot[NBUT_SAVE] , seq , NULL ) ;
13785        busy=0 ; RETURN(1) ;
13786      break ;
13787 
13788      case '3':
13789      case '#':{
13790        int rr = seq->render_mode ;
13791        if( !seq->allowmerger ){ busy=0 ; RETURN(1) ; }
13792 
13793             if( key == '3'             ) rr = 0 ;
13794        else if( rr  == RENDER_CHECK_OU ) rr = RENDER_CHECK_UO ;
13795        else                              rr = RENDER_CHECK_OU ;
13796        if( seq->render_scal != NULL ) ISQ_popdown_render_scal(seq) ;
13797        seq->render_mode = rr ;
13798        ISQ_redisplay( seq , -1 , isqDR_display ) ;
13799        ISQ_draw_winfo( seq ) ;
13800        busy=0 ; RETURN(1) ;
13801      }
13802      break ;
13803 
13804      case '$':
13805      case '%':
13806      case '4':
13807      case '5':
13808      case '6':{  /* 22 Aug 2014 */
13809        if( !seq->allowmerger ){ busy=0 ; RETURN(1) ; }
13810        if( seq->render_mode != 0 ){
13811          ISQ_popdown_render_scal(seq) ; seq->render_mode = 0 ; seq->render_fac = 0.0f ;
13812        } else {
13813          ISQ_popup_render_scal(seq) ;
13814          switch( key ){
13815            case '4': seq->render_mode = RENDER_WIPE_LEFT  ; break ;
13816            case '5': seq->render_mode = RENDER_WIPE_BOT   ; break ;
13817            case '6': seq->render_mode = RENDER_MIX        ; break ;
13818            case '$': seq->render_mode = RENDER_WIPE_RIGHT ; break ;
13819            case '%': seq->render_mode = RENDER_WIPE_TOP   ; break ;
13820          }
13821          ISQ_set_scale( seq->render_scal , 50 ) ; seq->render_fac = 0.50f ;
13822        }
13823        ISQ_redisplay( seq , -1 , isqDR_display ) ;
13824        ISQ_draw_winfo( seq ) ;
13825        busy=0 ; RETURN(1) ;
13826      }
13827      break ;
13828 
13829 #if 0
13830      case 'G':
13831      case 'H':
13832      case 'J':
13833      case 'K':{
13834        int mode = (key=='G') ? AGIF_MODE
13835                  :(key=='H') ? MPEG_MODE
13836                  :(key=='J') ? JPEG_MODE
13837                  :             PNG_MODE  ;
13838        ISQ_save_anim( seq , NULL , 0,0 , mode ) ;
13839        busy=0 ; RETURN(1) ;
13840      }
13841      break ;
13842 #endif
13843 
13844    } /* end of switch on character typed */
13845 
13846    busy=0; RETURN(0);
13847 }
13848 
13849 /*----------------------------------------------------------------------------*/
13850 /*! Carry out a scalar filter on the intensity component of an RGB image.
13851     Afterwards, the color for each pixel is rescaled from the old to the
13852     new by the filtered intensity.  This transform is done in-place.
13853 ------------------------------------------------------------------------------*/
13854 
mri_rgb_transform_nD(MRI_IMAGE * im,int ndim,generic_func * tfunc)13855 void mri_rgb_transform_nD( MRI_IMAGE *im, int ndim, generic_func *tfunc )
13856 {
13857    MRI_IMAGE *flim , *shim ;
13858    float *sar , *far ;
13859    int ii , nvox , rr,gg,bb ;
13860    float fac , smax,fmax,fsrat ;
13861 
13862 ENTRY("mri_rgb_transform_nD") ;
13863 
13864    if( im    == NULL || (im->kind != MRI_rgb && im->kind != MRI_rgba) ) EXRETURN ;  /* bad image? */
13865    if( tfunc == NULL || (ndim !=0            && ndim != 2           ) ) EXRETURN ;  /* bad tfunc? */
13866 
13867    flim = mri_to_float( im ) ;              /* input intensity image */
13868    fmax = mri_max( flim ) ;
13869    if( fmax == 0.0 ){ mri_free(flim); EXRETURN; }  /* nothing to do? */
13870 
13871    shim = mri_copy( flim ) ;        /* will be transformed intensity */
13872 
13873    switch( ndim ){               /* call in-place transform function */
13874      case 0:
13875        AFNI_CALL_0D_function( tfunc , shim->nvox , MRI_FLOAT_PTR(shim) ) ;
13876      break ;
13877 
13878      case 2:
13879        AFNI_CALL_2D_function( tfunc ,
13880                               shim->nx , shim->ny ,
13881                               shim->dx , shim->dy , MRI_FLOAT_PTR(shim) ) ;
13882      break ;
13883    }
13884 
13885    /* get scale factor to adjust for changes in overall amplitude */
13886 
13887    smax = mri_max(shim) ;
13888    if( smax == 0.0 ){ mri_free(flim); mri_free(shim); EXRETURN; }
13889    fsrat = fmax / smax ;
13890 
13891    far = MRI_FLOAT_PTR(flim) ; sar = MRI_FLOAT_PTR(shim) ;
13892 
13893    /* loop over pixels, adjusting color of each one by transformed intensity */
13894 
13895    switch( im->kind ){
13896      default: break ;   /* unreachable - just to stifle compiler warning */
13897      case MRI_rgb:{
13898        byte *iar = MRI_BYTE_PTR(im) ;
13899        nvox = im->nvox ;
13900        for( ii=0 ; ii < nvox ; ii++ ){
13901          if( far[ii] <= 0.0 || sar[ii] <= 0.0 ){       /* inten <= 0? */
13902            iar[3*ii] = iar[3*ii+1] = iar[3*ii+2] = 0 ;
13903          } else {
13904            fac = fsrat * sar[ii] / far[ii] ; /* will be positive */
13905            rr  = fac * iar[3*ii]   ; iar[3*ii  ] = (rr > 255) ? 255 : rr ;
13906            gg  = fac * iar[3*ii+1] ; iar[3*ii+1] = (gg > 255) ? 255 : gg ;
13907            bb  = fac * iar[3*ii+2] ; iar[3*ii+2] = (bb > 255) ? 255 : bb ;
13908          }
13909        }
13910      }
13911      break ;
13912 
13913      case MRI_rgba:{
13914        rgba *jar = MRI_RGBA_PTR(im) ;
13915        nvox = im->nvox ;
13916        for( ii=0 ; ii < nvox ; ii++ ){
13917          if( far[ii] <= 0.0 || sar[ii] <= 0.0 ){       /* inten <= 0? */
13918            jar[ii].r = jar[ii].g = jar[ii].b = 0 ;
13919          } else {
13920            fac = fsrat * sar[ii] / far[ii] ; /* will be positive */
13921            rr  = fac * jar[ii].r ; jar[ii].r = (rr > 255) ? 255 : rr ;
13922            gg  = fac * jar[ii].g ; jar[ii].g = (gg > 255) ? 255 : gg ;
13923            bb  = fac * jar[ii].b ; jar[ii].b = (bb > 255) ? 255 : bb ;
13924          }
13925        }
13926      }
13927      break ;
13928    }
13929 
13930    mri_free(flim) ; mri_free(shim) ;  /* toss the trash */
13931    EXRETURN ;
13932 }
13933 
13934 /*--------------------------------------------------------------------------*/
13935 /*! Save the current image to a file thru a filter.
13936     - Refactored from the former ISQ_save_jpeg() - 11 Dec 2006
13937     - Modified for use as a filter (no fname or suffix) - 14 Dec 2006 */
13938 
ISQ_save_image(MCW_imseq * seq,char * fname,char * filtername,char * suffix)13939 void ISQ_save_image( MCW_imseq *seq  , char *fname ,
13940                      char *filtername, char *suffix )
13941 {
13942    MRI_IMAGE *tim , *flim ;
13943    char fn[299], filt[512] ;
13944    FILE *fp ;
13945    int sll ;
13946 
13947 ENTRY("ISQ_save_image") ;
13948 
13949    if( !ISQ_REALZ(seq) || filtername == NULL ) EXRETURN;
13950 
13951    if( fname != NULL ){
13952      sll = strlen(fname) ; if( sll < 1 || sll > 255 ) EXRETURN ;
13953    }
13954    if( filtername == NULL ){
13955      if( fname == NULL ){ filtername = "cat > AFNI.ppm" ;             }
13956      else               { filtername = "cat > %s" ; suffix = ".ppm" ; }
13957    }
13958 
13959    /*-- get image that's stored for display, then process it --*/
13960 
13961    reload_DC_colordef( seq->dc ) ;
13962    tim = XImage_to_mri( seq->dc, seq->given_xim, X2M_USE_CMAP | X2M_FORCE_RGB );
13963    if( tim == NULL ) EXRETURN ;
13964 
13965    /** make the image square? **/
13966 
13967 /* INFO_message("AFNI_IMAGE_SAVESQUARE = %s",getenv("AFNI_IMAGE_SAVESQUARE")); */
13968    if( AFNI_yesenv("AFNI_IMAGE_SAVESQUARE") ){
13969      tim->dx = seq->last_dx ; tim->dy = seq->last_dy ;
13970      flim = mri_squareaspect( tim ) ;
13971      if( flim != NULL ){ mri_free(tim); tim = flim; }
13972    }
13973 
13974    /** zoom? **/
13975 
13976    if( DO_BLOWUP(seq) ){
13977      int zf = MAX(seq->zoom_fac,seq->saver_blowup) ;
13978      // [PT: Dec 19, 2018] Change default behavior to be NN interp
13979      if( AFNI_noenv("AFNI_IMAGE_ZOOM_NN") ) mri_dup2D_mode(-7) ;
13980      flim = mri_dup2D(zf,tim) ;
13981      mri_dup2D_mode(7) ;
13982      if( flim != NULL ){ mri_free(tim); tim = flim; }
13983    }
13984 
13985    /** line drawing overlay? **/
13986 
13987    if( seq->mplot != NULL ){
13988      /* mri_draw_force_opaque(1) ; */
13989      memplot_to_mri_set_dothick(1) ;
13990      memplot_to_RGB_sef( tim, seq->mplot, 0,0,MEMPLOT_FREE_ASPECT ) ;
13991      memplot_to_mri_set_dothick(0) ;
13992      /* mri_draw_force_opaque(0) ; */
13993    }
13994 
13995    /** cut up zoomed image? **/
13996 
13997    if( seq->zoom_fac      > 1           &&
13998        seq->saver_blowup == 1           &&
13999        seq->mont_nx      == 1           &&
14000        seq->mont_ny      == 1           &&
14001        AFNI_yesenv("AFNI_CROP_ZOOMSAVE")  ) {
14002 
14003       int xa,ya , iw=tim->nx/seq->zoom_fac , ih=tim->ny/seq->zoom_fac ;
14004 
14005       xa = seq->zoom_hor_off * tim->nx ;
14006       if( xa+iw > tim->nx ) xa = tim->nx-iw ;
14007       ya = seq->zoom_ver_off * tim->nx ;
14008       if( ya+ih > tim->ny ) ya = tim->ny-ih ;
14009       flim = mri_cut_2D( tim , xa,xa+iw-1 , ya,ya+ih-1 ) ;
14010       if( flim != NULL ){ mri_free(tim); tim = flim; }
14011    }
14012 
14013    /** open a pipe to the filter function **/
14014 
14015    if( fname != NULL ){
14016      strcpy(fn,fname) ;
14017      if( suffix != NULL && *suffix != '\0' &&
14018          !STRING_HAS_SUFFIX_CASE(fname,suffix) ){
14019        if( *suffix != '.' ) strcat(fn,".") ;
14020        strcat(fn,suffix) ;
14021      }
14022      sprintf( filt , filtername , fn ) ;
14023    } else {
14024      strcpy( filt , filtername ) ;
14025    }
14026    INFO_message("Writing one %dx%d image to filter '%s'",tim->nx,tim->ny,filt) ;
14027 
14028 #ifndef CYGWIN
14029    signal( SIGPIPE , SIG_IGN ) ;
14030 #endif
14031    errno = 0 ; fp = popen( filt , "w" ) ;
14032    if( fp == NULL ){
14033      ERROR_message("Can't open output filter: %s",filt) ;
14034      if( errno != 0 ) perror("** Unix error message") ;
14035      mri_free(tim) ; EXRETURN ;
14036    }
14037 
14038    /** write a PPM file to the filter pipe **/
14039 
14040    fprintf(fp,"P6\n%d %d\n255\n" , tim->nx,tim->ny ) ;
14041    fwrite( MRI_RGB_PTR(tim), sizeof(byte), 3*tim->nvox, fp ) ; fflush(fp) ;
14042    errno = 0 ; sll = pclose(fp) ;
14043    if( sll == -1 ){
14044      ERROR_message("Image save filter command was %s\n",filt) ;
14045      if( errno != 0 ) perror("** Unix error in image output pipe") ;
14046    }
14047 
14048    mri_free(tim) ; EXRETURN ;
14049 }
14050 
14051 /*--------------------------------------------------------------------------*/
14052 
ISQ_save_jpeg(MCW_imseq * seq,char * fname)14053 void ISQ_save_jpeg( MCW_imseq *seq , char *fname )
14054 {
14055    ISQ_save_image( seq , fname , ppmto_jpg95_filter , ".jpg" ) ;
14056    return ;
14057 }
14058 
ISQ_save_png(MCW_imseq * seq,char * fname)14059 void ISQ_save_png( MCW_imseq *seq , char *fname )  /* 11 Dec 2006 */
14060 {
14061    ISQ_save_image( seq , fname , ppmto_png_filter , ".png" ) ;
14062    return ;
14063 }
14064 
14065 /*--------------------------------------------------------------------------*/
14066 
ISQ_save_raw(MCW_imseq * seq,char * fname)14067 void ISQ_save_raw( MCW_imseq *seq , char *fname )  /* 13 Nov 2007 */
14068 {
14069    MRI_IMAGE *im ;
14070 
14071 ENTRY("ISQ_save_raw") ;
14072    if( !ISQ_REALZ(seq) ) EXRETURN ;
14073    if( fname == NULL || *fname == '\0' ) fname = "image.raw" ;
14074 
14075    im = ISQ_getimage( seq->im_nr , seq ) ;
14076 
14077    if( im != NULL ){
14078      INFO_message("Writing one %dx%d raw image (type=%s bytes=%d) to file '%s'",
14079                   im->nx,im->ny,MRI_TYPE_name[im->kind],im->nvox*im->pixel_size,fname ) ;
14080      mri_write_raw(fname,im); mri_free(im);
14081    }
14082    EXRETURN ;
14083 }
14084 
14085 /*--------------------------------------------------------------------------*/
14086 
ISQ_save_rawmont(MCW_imseq * seq,char * fname)14087 void ISQ_save_rawmont( MCW_imseq *seq , char *fname ) /* 13 Nov 2007 */
14088 {
14089    MRI_IMAGE *im ;
14090    MRI_IMARR *mar ;
14091    int nmont=seq->mont_nx * seq->mont_ny ,ij,nim,ijcen,nxyim ;
14092 
14093 ENTRY("ISQ_save_raw_montage") ;
14094    if( !ISQ_REALZ(seq) ) EXRETURN ;
14095 
14096    if( nmont < 2 ){
14097      INFO_message("save_rawmont: montage not turned on") ;
14098      ISQ_save_raw(seq,fname); EXRETURN;
14099    }
14100 
14101    if( fname == NULL || *fname == '\0' ) fname = "image_montage.raw" ;
14102 
14103    /* the following code is mostly from ISQ_make_montage() */
14104 
14105    INIT_IMARR(mar) ;
14106 
14107    ijcen = (seq->mont_nx)/2 + (seq->mont_ny/2) * seq->mont_nx ;
14108    for( nxyim=ij=0 ; ij < nmont ; ij++ ){
14109       nim = seq->im_nr + (seq->mont_skip + 1) * (ij - ijcen) ;
14110       im  = ISQ_getimage( nim , seq ) ; if( im != NULL ) nxyim++ ;
14111       ADDTO_IMARR(mar,im) ;
14112    }
14113    if( nxyim == 0 ){
14114      ERROR_message("Raw montage error: no images found!") ;
14115      DESTROY_IMARR(mar) ; EXRETURN ;
14116    }
14117 
14118    im = mri_cat2D( seq->mont_nx , seq->mont_ny , 0 , NULL , mar ) ;
14119    DESTROY_IMARR(mar) ;
14120 
14121    if( im != NULL ){
14122      INFO_message("Writing one %dx%d raw image (type=%s bytes=%d) to file '%s'",
14123                   im->nx,im->ny,MRI_TYPE_name[im->kind],im->nvox*im->pixel_size,fname ) ;
14124      mri_write_raw(fname,im); mri_free(im);
14125    } else {
14126      ERROR_message("Can't make raw montage for some reason!") ;
14127    }
14128 
14129    EXRETURN ;
14130 }
14131 
14132 /*--------------------------------------------------------------------------*/
14133 /*! Save the current images to an MPEG or AGIF file.  [06 Dec 2006]
14134       Say the recorder has N images       ZSS Jan 07
14135    if top < 0 then top = N +top
14136       top ==0 then top = N -1
14137    else top = min(top, N)
14138    if bot < 0 then bot = N +bot
14139    else bot = max(bot, 0)
14140 
14141    use bot = 0 and top = 0 to save everything.
14142 */
14143 
ISQ_save_anim(MCW_imseq * seq,char * prefin,int bot,int top,int mode)14144 void ISQ_save_anim( MCW_imseq *seq, char *prefin, int bot, int top, int mode )
14145 {
14146    int ii , kf , ll ;
14147    MRI_IMAGE *tim , *flim ;
14148    char *fnamep=NULL, *prefix ;
14149    THD_string_array *agif_list=NULL ;
14150    char tsuf[8] ;
14151    float dx,dy ;
14152 #ifndef USE_GIFF
14153    char *togif = ppmto_gif_filter ;
14154 #else
14155    char *togif = ppmto_giff_filter ;
14156 #endif
14157    int doanim=0 ;
14158    char filt[512], *ppo = NULL; FILE *fp ; MRI_IMAGE *ovim ;
14159    int nx , ny , npix , pc ;
14160    int adup=1 , akk,aa ;        /* 10 Feb 2009 */
14161 
14162 ENTRY("ISQ_save_anim") ;
14163 
14164    if( !ISQ_REALZ(seq) ) EXRETURN ;  /* bad input */
14165    switch( mode ){
14166      default: EXRETURN ;             /* bad input */
14167 
14168      case AGIF_MODE:
14169        if( ppmto_agif_filter == NULL || togif == NULL ){
14170          ERROR_message("Can't save AGIF - missing filter!\a") ; EXRETURN ;
14171        }
14172        doanim = 1 ;
14173      break ;
14174 
14175      case MPEG_MODE:
14176        if( ppmto_mpeg_filter == NULL || ppmto_ppm_filter == NULL ){
14177          ERROR_message("Can't save MPEG - missing filter!\a") ; EXRETURN ;
14178        }
14179        doanim = 1 ;
14180      break ;
14181 
14182      case JPEG_MODE:
14183        if( ppmto_jpg95_filter == NULL ){
14184          ERROR_message("Can't save JPEG - missing filter!\a") ; EXRETURN ;
14185        }
14186        doanim = 0 ;
14187      break ;
14188 
14189      case PNG_MODE:
14190        if( ppmto_png_filter == NULL ){
14191          ERROR_message("Can't save PNG - missing filter!\a") ; EXRETURN ;
14192        }
14193        doanim = 0 ;
14194      break ;
14195    }
14196 
14197   if (bot < 0) { /* special case */
14198       bot = seq->status->num_total+bot ;
14199       if (bot < 0) bot = 0;
14200    } else {
14201       bot = MAX(bot,0) ;
14202    }
14203    if( top < 0 ) {
14204       top = seq->status->num_total+top ;
14205       if (top > seq->status->num_total-1) top = seq->status->num_total-1;
14206    } else if (top == 0) {
14207       top = seq->status->num_total -1;
14208    }  else  {
14209       top = MIN(top,seq->status->num_total-1) ;
14210    }
14211    if( bot > top || (bot==top && doanim) ){
14212      ERROR_message("Can't save image range %d..%d!\a",bot,top) ; EXRETURN ;
14213    }
14214 
14215    /*
14216       fprintf(stderr,
14217          "+++ Will save from %d to %d with %d images total in recorder.\n",
14218                bot, top, seq->status->num_total);
14219    */
14220    /*--- setup prefix for animation filename to save ---*/
14221 
14222    if( prefin == NULL || *prefin == '\0' ) prefin = "Anim" ;
14223    if( !THD_filename_ok(prefin) ){
14224      ERROR_message("Bad image save filename '%s'\a",prefin) ; EXRETURN ;
14225    }
14226    ll = strlen(prefin) ;
14227    prefix = (char*)calloc( ll+16, sizeof(char)) ;
14228    strcpy( prefix , prefin ) ;
14229    fnamep = (char*)calloc( ll+32,  sizeof(char)) ;
14230 
14231    ppo = THD_trailname(prefix,0) ;               /* strip directory */
14232 
14233    if( prefix[ll-1] != '.' ){  /* add a . at the end */
14234      prefix[ll++] = '.' ;      /* if one isn't there */
14235      prefix[ll]   = '\0' ;
14236    }
14237 
14238    tsuf[0] = (lrand48()>>5)%26 + 'A' ;   /* random suffix */
14239    tsuf[1] = (lrand48()>>5)%26 + 'A' ;   /* for animation */
14240    tsuf[2] = (lrand48()>>5)%26 + 'A' ;   /* temp files    */
14241    tsuf[3] = '\0' ;
14242 
14243 #ifdef USE_GIFF          /* create the fixed GIF colormap for animations */
14244    if( mode == AGIF_MODE ){
14245      MRI_IMAGE *im = mri_colorsetup( 76 , 6,6,5 ); /* 76 grays + */
14246      remove( GIFF_MAPFILE ) ;                     /* 6*red X 6*green X 5*blue */
14247      mri_write_pnm( GIFF_MAPFILE , im ) ;
14248      mri_free( im ) ;
14249    }
14250 #endif
14251 
14252    if( mode == AGIF_MODE || mode == MPEG_MODE ){
14253      adup = (ISQ_anim_dup > 0) ? ISQ_anim_dup : AFNI_numenv("AFNI_ANIM_DUP") ;
14254      if( adup <= 0 ) adup = 1 ; else if( adup > 99 ) adup = 99 ;
14255    }
14256 
14257    /*---- loop thru, get images, save them ----*/
14258 
14259    if( doanim )
14260      INFO_message("Starting to save images to temp files") ;
14261    else
14262      INFO_message("Starting to save images") ;
14263 
14264    for( akk=0,kf=bot ; kf <= top ; kf++ ){
14265 
14266       /* get the underlay image */
14267 
14268       tim = ISQ_getimage( kf , seq ) ;
14269 
14270       /* if we failed to get the image? */
14271 
14272       if( tim == NULL ) continue ;  /* skip to next one? */
14273 
14274       /* image to save will be in flim */
14275 
14276       flim = tim ;
14277 
14278       /* process image to make the grayscale index */
14279 
14280       seq->set_orim = 0 ;
14281       tim  = flim ;
14282       flim = ISQ_process_mri( kf , seq , tim , 0 ) ;
14283       if( tim != flim ) KILL_1MRI( tim ) ;
14284 
14285       /* get overlay and flip it */
14286 
14287       ovim = NULL ;
14288       if( !ISQ_SKIP_OVERLAY(seq) ){
14289         tim = ISQ_getoverlay( kf , seq ) ;
14290         if( tim != NULL && !ISQ_GOOD_OVERLAY_TYPE(tim->kind) ){
14291           KILL_1MRI(tim) ;
14292         }
14293         if( tim != NULL )
14294          ovim = mri_flippo( ISQ_TO_MRI_ROT(seq->opt.rot), seq->opt.mirror, tim );
14295         if( tim != ovim ) KILL_1MRI(tim) ;
14296         ISQ_apply_mask( seq->last_automask , ovim ) ;
14297       }
14298 
14299       /* and perform overlay onto flim */
14300 
14301       if( ovim != NULL ){
14302         tim = flim ;
14303         flim = ISQ_overlay( seq->dc , tim , ovim , seq->ov_opacity ) ;
14304         if( flim == NULL ){ flim = tim ; }     /* shouldn't happen */
14305         else              { KILL_1MRI(tim) ; }
14306         mri_free( ovim ) ;
14307       }
14308 
14309 /* INFO_message("AFNI_IMAGE_SAVESQUARE = %s",getenv("AFNI_IMAGE_SAVESQUARE")); */
14310       if( AFNI_yesenv("AFNI_IMAGE_SAVESQUARE") ){   /* 08 Jun 2004 */
14311         flim->dx = seq->last_dx ; flim->dy = seq->last_dy ;
14312         tim = mri_squareaspect( flim ) ;
14313         if( tim != NULL ){ mri_free(flim); flim = tim; }
14314       }
14315 
14316       /* if needed, convert from indices to RGB */
14317 
14318       if( flim->kind == MRI_short ){
14319         tim = ISQ_index_to_rgb( seq->dc , 0 , flim ) ;
14320         mri_free(flim) ; flim = tim ;
14321       }
14322 
14323       /* 26 Mar 2002: zoom out, and geometry overlay, maybe */
14324 
14325       if( DO_BLOWUP(seq) ){
14326         int zf = MAX(seq->zoom_fac,seq->saver_blowup) ;
14327         // [PT: Dec 19, 2018] Change default behavior to be NN interp
14328         if( AFNI_noenv("AFNI_IMAGE_ZOOM_NN") ) mri_dup2D_mode(-7) ;
14329         tim = mri_dup2D(zf,flim) ;
14330         mri_dup2D_mode(7) ;
14331         mri_free(flim) ; flim = tim ;
14332       }
14333 
14334       if( MCW_val_bbox(seq->wbar_plots_bbox) != 0 ){  /* draw geometry overlay */
14335         MEM_plotdata *mp ;
14336         mp = ISQ_getmemplot( kf , seq ) ;
14337         if( mp != NULL ){
14338           flip_memplot( ISQ_TO_MRI_ROT(seq->opt.rot),seq->opt.mirror,mp );
14339           /* mri_draw_force_opaque(1) ; */
14340           memplot_to_RGB_sef( flim, mp, 0,0,MEMPLOT_FREE_ASPECT ) ;
14341           /* mri_draw_force_opaque(0) ; */
14342           delete_memplot(mp) ;
14343         }
14344       }
14345 
14346       if( seq->wbar_label_av->ival != 0 ){  /* 17 Jun 2005 */
14347         char *lab = ISQ_getlabel( kf , seq ) ;
14348         if( lab != NULL ){
14349           MEM_plotdata *mp = ISQ_plot_label( seq , lab ) ;
14350           if( mp != NULL ){
14351             /* mri_draw_force_opaque(1) ; */
14352             memplot_to_mri_set_dothick(1) ;
14353             memplot_to_RGB_sef( flim, mp, 0,0,MEMPLOT_FREE_ASPECT ) ;
14354             memplot_to_mri_set_dothick(0) ;
14355             /* mri_draw_force_opaque(0) ; */
14356             delete_memplot(mp) ;
14357           }
14358           free(lab) ;
14359         }
14360       }
14361 
14362       if( seq->zoom_fac > 1 &&                   /* crop zoomed image */
14363           seq->mont_nx == 1 &&                   /* to displayed part? */
14364           seq->mont_ny == 1 &&
14365           AFNI_yesenv("AFNI_CROP_ZOOMSAVE") ) {
14366 
14367         int xa,ya , iw=flim->nx/seq->zoom_fac , ih=flim->ny/seq->zoom_fac ;
14368 
14369         xa = seq->zoom_hor_off * flim->nx ;
14370         if( xa+iw > flim->nx ) xa = flim->nx-iw ;
14371         ya = seq->zoom_ver_off * flim->nx ;
14372         if( ya+ih > flim->ny ) ya = flim->ny-ih ;
14373         tim = mri_cut_2D( flim , xa,xa+iw-1 , ya,ya+ih-1 ) ;
14374         if( tim != NULL ){ mri_free(flim); flim = tim; }
14375       }
14376 
14377       /* image dimensions we are saving */
14378 
14379       nx = flim->nx ; ny = flim->ny ; npix = nx*ny ;
14380 
14381       /* create the filter command into string 'filt' */
14382 
14383       for( aa=0 ; aa < adup ; aa++,akk++ ){ /* adup==1 if no animation */
14384 
14385         switch( mode ){
14386           case AGIF_MODE:
14387             sprintf( fnamep, "%s%s.%05d.gif" , prefix,tsuf, akk) ;
14388             sprintf( filt , togif  , fnamep ) ;  /* free colormap */
14389             if( agif_list == NULL ) INIT_SARR(agif_list) ;
14390             ADDTO_SARR(agif_list,fnamep) ;
14391           break ;
14392 
14393           case MPEG_MODE:
14394             sprintf( fnamep, "%s%s.%06d.ppm" , ppo,tsuf, akk) ;
14395             sprintf( filt , ppmto_ppm_filter , fnamep ) ;
14396             if( agif_list == NULL ) INIT_SARR(agif_list) ;
14397             ADDTO_SARR(agif_list,fnamep) ;
14398           break ;
14399 
14400           case JPEG_MODE:
14401             sprintf( fnamep, "%s%05d.jpg" , prefix, kf) ;
14402             sprintf( filt , ppmto_jpg95_filter , fnamep ) ;
14403             if( agif_list == NULL ) INIT_SARR(agif_list) ;
14404             ADDTO_SARR(agif_list,fnamep) ;
14405           break ;
14406 
14407           case PNG_MODE:
14408             sprintf( fnamep, "%s%05d.png" , prefix, kf) ;
14409             sprintf( filt , ppmto_png_filter , fnamep ) ;
14410             if( agif_list == NULL ) INIT_SARR(agif_list) ;
14411             ADDTO_SARR(agif_list,fnamep) ;
14412           break ;
14413         }
14414 #ifndef CYGWIN
14415         signal( SIGPIPE , SIG_IGN ) ;                 /* ignore broken pipe */
14416 #endif
14417         fp = popen( filt , "w" ) ;                    /* open pipe to filter */
14418         if( fp == NULL ){
14419           ERROR_message("Can't open output filter %s\a",filt) ;
14420           break ;  /* out of loop over aa */
14421         }
14422 
14423         /* write RGB image to pipe as a PPM file */
14424 
14425         fprintf(fp,"P6\n%d %d\n255\n" , nx,ny ) ;
14426         fwrite( MRI_RGB_PTR(flim), sizeof(byte), 3*npix, fp ) ;
14427         pc = pclose(fp) ;
14428         if( pc == -1 ) perror("Error in image output pipe") ;
14429 
14430       } /* end of loop over aa = image duplicates for animation */
14431 
14432       /* done with this image */
14433 
14434       mri_free(flim) ; flim = NULL ;
14435 
14436    } /* end of loop over image sequence to save */
14437 
14438    /** post-process saved images into animation? **/
14439 
14440    if( agif_list != NULL && agif_list->num > 0 && doanim ){
14441 
14442      int af ;
14443 
14444      switch( mode ){
14445       /* animated GIF */
14446 
14447       case AGIF_MODE:{
14448         int alen ; char *alc , *alf , *oof ;
14449 #ifdef USE_GIFF
14450         remove( GIFF_MAPFILE ) ;   /* don't need this any longer */
14451 #endif
14452         for( alen=af=0 ; af < agif_list->num ; af++ ) /* size of all */
14453           alen += strlen( agif_list->ar[af] ) ;       /* filenames  */
14454 
14455         alen += 3*agif_list->num + 32 ;               /* all filenames */
14456         alc = AFMALL ( char, alen) ; alc[0] = '\0' ;  /* in one string */
14457         for( alen=af=0 ; af < agif_list->num ; af++ ){
14458           strcat(alc," ") ; strcat(alc,agif_list->ar[af]) ;
14459         }
14460 
14461         oof  = AFMALL( char, strlen(prefix)+32 ) ; /* output fname */
14462         sprintf(oof,"%sgif",prefix) ;
14463 
14464         alen =  strlen(alc)+strlen(ppmto_agif_filter)+strlen(oof)+32 ;
14465         alf  = AFMALL( char, alen) ;
14466         sprintf(alf , ppmto_agif_filter, alc, oof ) ; /* command to run */
14467         INFO_message("Running '%s'",alf) ;
14468         if( THD_is_ondisk(oof) ) WARNING_message("Over-writing '%s'",oof);
14469         system(alf) ;                                 /* so run it!    */
14470         free(alf) ; free(oof) ; free(alc) ;           /* free trash   */
14471       }
14472       break ;
14473 
14474       /* MPEG-1 */
14475 
14476       case MPEG_MODE:{
14477         int alen ; char *alf , *oof , *frate ;
14478         char *qscale , *pattrn ;
14479 
14480         /* compose ffmpeg parameters */
14481         oof = AFMALL( char, strlen(prefix)+32 ) ; /* output fname */
14482         sprintf(oof,"%smpg",prefix) ;
14483         qscale=getenv("AFNI_MPEG_QSCALE")   ; if(qscale==NULL) qscale="11";
14484         frate =getenv("AFNI_MPEG_FRAMERATE"); if(frate ==NULL) frate ="24";
14485         pattrn=getenv("AFNI_MPEG_PATTERN")  ;
14486         if( pattrn == NULL ){
14487             /* by default use only I-frames */
14488             if( adup <= 1 ) pattrn="-intra";
14489             /* otherwise go with ffmpegs default */
14490             else pattrn="";
14491         }
14492         /* make command to run */
14493         alen = strlen(seq->saver_prefix) + strlen(ppmto_mpeg_filter)
14494                       +1000 ;
14495         alf  = AFMALL( char, alen) ;
14496         sprintf(alf,
14497           "%s -r %s -f image2 -i %s%s.%%06d.ppm -b 400k -qscale %s %s %s",
14498           ppmto_mpeg_filter, frate, seq->saver_prefix, tsuf, qscale,
14499           pattrn, oof) ; /* command to run */
14500         INFO_message("Running '%s' to produce %s",alf,oof) ;
14501         if( THD_is_ondisk(oof) ) WARNING_message("Over-writing '%s'",oof);
14502         system(alf) ;                            /* so run it!    */
14503         free(alf); free(oof); /* free trash   */
14504       }
14505       break ;
14506      }
14507 
14508      /* animation is done, for good or for ill */
14509 
14510      for( af=0 ; af < agif_list->num ; af++ )  /* erase temp files */
14511        remove( agif_list->ar[af] ) ;
14512      INFO_message("Done saving images") ;
14513 
14514    } else if( agif_list != NULL && agif_list->num > 0 ){
14515      if( agif_list->num > 1 )
14516        INFO_message("%d images saved in files %s .. %s",
14517                     agif_list->num ,
14518                     agif_list->ar[0] , agif_list->ar[agif_list->num-1] ) ;
14519      else
14520        INFO_message("1 image saved in file %s",agif_list->ar[0]) ;
14521    }
14522 
14523    /*--- go home ---*/
14524 
14525    DESTROY_SARR(agif_list) ; free(prefix) ; free(fnamep); EXRETURN ;
14526 }
14527 
14528 /*----------------------------------------------------------------------------*/
14529 /**** Stuff for the fun fun fun VG effect [RWC Feb 2017] ****/
14530 
mri_streakize(MRI_IMAGE * im,MRI_IMAGE * sxim,MRI_IMAGE * syim)14531 static MRI_IMAGE * mri_streakize( MRI_IMAGE *im , MRI_IMAGE *sxim , MRI_IMAGE *syim )
14532 {
14533    MRI_IMAGE *qim ; byte *qar , *iar ;
14534    float *sxar , *syar ;
14535    int nx,ny,nxy , kk,dk , ii,jj,sk, dd,di,dj , ei,ej , ns ;
14536    float strk , sx,sy , rr,gg,bb , bsig,slo,shi ;
14537 
14538    nx = im->nx ; ny = im->ny ; nxy = nx*ny ;
14539    bsig = sqrtf(nx*(float)ny) ;
14540 
14541    /* min and max streak sizes */
14542    slo  = 0.004f*bsig ; if( slo < 2.0f     ) slo = 2.0f ;
14543    shi  = 0.024f*bsig ; if( shi < 6.0f*slo ) shi = 6.0f*slo ;
14544 
14545    qim = mri_copy(im) ; qar = MRI_RGB_PTR(qim) ; iar = MRI_RGB_PTR(im) ;
14546    sxar = MRI_FLOAT_PTR(sxim) ; syar = MRI_FLOAT_PTR(syim) ;
14547 
14548 /* ININFO_message("mri_streakize") ; */
14549 
14550    for( kk=0 ; kk < nxy ; kk++ ){
14551      /* get streak vector */
14552      sx = sxar[kk] ; sy = syar[kk] ;
14553      strk = sqrtf(sx*sx+sy*sy) ;
14554      if( strk == 0.0f ){
14555        sx = (2.0f*drand48()-1.0f)*slo ;
14556        sy = (2.0f*drand48()-1.0f)*slo ; strk = sqrtf(sx*sx+sy*sy);
14557      } else if( strk < slo ){
14558        sx *= (slo/strk) ; sy *= (slo/strk) ; strk = slo ;
14559      }
14560      sx /= strk ; sy /= strk ; /* unit vector */
14561      if( strk < slo ) strk = slo; else if( strk > shi ) strk = shi ;
14562      sk = (int)(strk+0.499f) ;
14563      /* color at start pixel */
14564      rr = iar[3*kk+0]; gg = iar[3*kk+1]; bb = iar[3*kk+2]; ns = 1;
14565      ii = kk % nx ; jj = kk / nx ;
14566      for( dd=1 ; dd <= sk ; dd++ ){ /* streaking */
14567        di = (int)(dd*sx+0.499f) ; dj = (int)(dd*sy+0.499f) ;
14568        /* if( di == 0.0f && dj == 0.0f ) continue ; */
14569        ei = ii+di ; ej = jj+dj ;    /* the plus step */
14570        if( ei >= 0 && ei < nx && ej >= 0 && ej < ny ){
14571          dk = ei + ej*nx ;
14572          rr += iar[3*dk+0] ; gg += iar[3*dk+1] ; bb += iar[3*dk+2] ; ns++ ;
14573        }
14574        ei = ii-di ; ej = jj-dj ;    /* the minus step */
14575        if( ei >= 0 && ei < nx && ej >= 0 && ej < ny ){
14576          dk = ei + ej*nx ;
14577          rr += iar[3*dk+0] ; gg += iar[3*dk+1] ; bb += iar[3*dk+2] ; ns++ ;
14578        }
14579      }
14580      if( ns > 1 ){  /* if we summed in any other pixels */
14581        rr /= ns ; gg /= ns ; bb /= ns ;
14582        qar[3*kk+0] = BYTEIZE(rr) ; qar[3*kk+1] = BYTEIZE(gg) ; qar[3*kk+2] = BYTEIZE(bb) ;
14583      }
14584    }
14585 
14586    return qim ;
14587 }
14588 
14589 /*----------------------------------------------------------------------------*/
14590 
mri_vgize(MRI_IMAGE * iim)14591 static MRI_IMAGE * mri_vgize( MRI_IMAGE *iim )
14592 {
14593    MRI_IMAGE *blim , *gxim,*gyim , *bxim,*byim , *im ;
14594    float     *bar , *gxar,*gyar , *bxar,*byar ;
14595    int nx,ny,nxy , ii,jj,kk,joff ;
14596    float bsig , bmax , gsiz , blen , bx,by , slen , cc,ss ;
14597    byte *iar ;
14598 
14599    if( iim == NULL ) return NULL ;
14600 
14601    im = mri_to_rgb(iim) ; iar = MRI_RGB_PTR(im) ;
14602 
14603    nx = im->nx ; ny = im->ny ; nxy = nx*ny ;
14604 
14605    bsig = sqrtf(nx*(float)ny) * vgize_sigfac ;
14606    if( bsig < 1.9f ) bsig = 1.9f ;
14607 /* INFO_message("mri_vgize: nx=%d ny=%d bsig=%.3f",nx,ny,bsig) ; */
14608 
14609 #define NOIS_SIZ  27.0f
14610 
14611    /* add colored noise to the image */
14612 #ifdef NOIS_SIZ
14613  if( ! AFNI_noenv("AFNI_VG_RANCOLOR") ){
14614    MRI_IMAGE *rrim , *ggim , *bbim , *qqim ;
14615    float     *rrar , *ggar , *bbar , rmax,gmax,bmax , rr,gg,bb,qq , nois ;
14616    rrim = mri_new_conforming(im,MRI_float) ; rrar = MRI_FLOAT_PTR(rrim) ;
14617    ggim = mri_new_conforming(im,MRI_float) ; ggar = MRI_FLOAT_PTR(ggim) ;
14618    bbim = mri_new_conforming(im,MRI_float) ; bbar = MRI_FLOAT_PTR(bbim) ;
14619    nois = NOIS_SIZ + 222.2f*vgize_sigfac ;
14620    for( kk=0 ; kk < nxy ; kk++ ){
14621      rrar[kk] = (float)(11.0*drand48()-5.0) ;
14622      ggar[kk] = (float)(10.0*drand48()-5.0) ;
14623      bbar[kk] = (float)(11.0*drand48()-5.0) ;
14624    }
14625    qqim = mri_float_blur2D(0.4f*bsig,rrim); mri_free(rrim); rrim = qqim; rrar = MRI_FLOAT_PTR(rrim);
14626    qqim = mri_float_blur2D(0.4f*bsig,ggim); mri_free(ggim); ggim = qqim; ggar = MRI_FLOAT_PTR(ggim);
14627    qqim = mri_float_blur2D(0.4f*bsig,bbim); mri_free(bbim); bbim = qqim; bbar = MRI_FLOAT_PTR(bbim);
14628    rmax = gmax = bmax = 0.0f ;
14629    for( kk=0 ; kk < nxy ; kk++ ){
14630      qq = fabsf(rrar[kk]) ; if( qq > rmax ) rmax = qq ;
14631      qq = fabsf(ggar[kk]) ; if( qq > gmax ) gmax = qq ;
14632      qq = fabsf(bbar[kk]) ; if( qq > bmax ) bmax = qq ;
14633    }
14634    rmax = NOIS_SIZ/rmax ; gmax = NOIS_SIZ/gmax ; bmax = NOIS_SIZ/bmax ;
14635 
14636    for( kk=0 ; kk < nxy ; kk++ ){
14637      rr = (float)iar[3*kk+0] + rrar[kk]*rmax; iar[3*kk+0] = BYTEIZE(rr);
14638      gg = (float)iar[3*kk+1] + ggar[kk]*gmax; iar[3*kk+1] = BYTEIZE(gg);
14639      bb = (float)iar[3*kk+2] + bbar[kk]*bmax; iar[3*kk+2] = BYTEIZE(bb);
14640    }
14641    mri_free(rrim); mri_free(ggim); mri_free(bbim);
14642  }
14643 #endif
14644 
14645    bxim = mri_to_float(im) ;
14646    blim = mri_float_blur2D( bsig , bxim ) ; mri_free(bxim) ;
14647    bar  = MRI_FLOAT_PTR(blim) ;
14648 
14649    gxim = mri_new_conforming(blim,MRI_float) ; gxar = MRI_FLOAT_PTR(gxim) ;
14650    gyim = mri_new_conforming(blim,MRI_float) ; gyar = MRI_FLOAT_PTR(gyim) ;
14651 
14652 /* ININFO_message("compute gradients") ; */
14653    for( jj=0 ; jj < ny ; jj++ ){
14654     joff = jj*nx ;
14655     for( ii=0 ; ii < nx ; ii++ ){
14656       if( jj==0 || jj==ny-1 || ii==0 || ii==nx-1 ){
14657         gxar[ii+joff] = gyar[ii+joff] = 0.0f ;
14658       } else {
14659         gxar[ii+joff] = bar[ii+joff+1 ] - bar[ii+joff-1 ] ;
14660         gyar[ii+joff] = bar[ii+joff+nx] - bar[ii+joff-nx] ;
14661       }
14662    }}
14663 
14664 /* ININFO_message("blur gradients") ; */
14665    bxim = mri_float_blur2D(0.5f*bsig,gxim); mri_free(gxim); bxar = MRI_FLOAT_PTR(bxim);
14666    byim = mri_float_blur2D(0.5f*bsig,gyim); mri_free(gyim); byar = MRI_FLOAT_PTR(byim);
14667 
14668 /* ININFO_message("find gradient max") ; */
14669    bmax = 0.0f ;
14670    for( kk=0 ; kk < nxy ; kk++ ){
14671      gsiz = bxar[kk]*bxar[kk] + byar[kk]*byar[kk] ;
14672      if( gsiz > bmax ) bmax = gsiz ;
14673    }
14674    bmax = sqrtf(bmax) ;
14675 /* ININFO_message("bmax=%g",bmax) ; */
14676    if( bmax == 0.0f ){ mri_free(bxim); mri_free(byim); mri_free(im); return NULL; }
14677 
14678    /* scale gradients by largest one */
14679 
14680    bmax = 1.0f / bmax ;
14681    slen = 1.3f * bsig ;
14682    for( kk=0 ; kk < nxy ; kk++ ){
14683      bx = bxar[kk]*bmax ; by = byar[kk]*bmax ; gsiz = sqrtf(bx*bx+by*by) ;
14684      if( gsiz > 0.0f ){
14685        cc = 0.111f + 2.69f*gsiz ; if( cc > 1.0f ) cc = 1.0f ;
14686        bx *= (cc*slen/gsiz) ; by *= (cc*slen/gsiz) ;
14687      }
14688      bxar[kk] = by ; byar[kk] = -bx ;          /* streak direction */
14689      bar[kk]  = (float)(30.0*drand48()-15.0) ; /* random angle for streak */
14690    }
14691 
14692 /* ININFO_message("blur angles") ; */
14693    gxim = mri_float_blur2D(0.5f*bsig,blim); mri_free(blim); gxar = MRI_FLOAT_PTR(gxim);
14694    bmax = 0.0f ;
14695    for( kk=0 ; kk < nxy ; kk++ ){
14696      gsiz = fabsf(gxar[kk]) ; if( gsiz > bmax ) bmax = gsiz ;
14697    }
14698 /* ININFO_message("max angle=%g",bmax) ; */
14699 
14700    /* rotate streak directions randomly */
14701    if( bmax > 0.0f && vgize_sigfac > 0.0111f ){
14702      bmax = (22.2f * PI/180.0f) / bmax ;  /* max angle is 22.2 degrees */
14703      for( kk=0 ; kk < nxy ; kk++ ){
14704        bx = bxar[kk] ; by = byar[kk] ;
14705        gsiz = sqrtf(bx*bx+by*by) ;
14706        cc = cosf(bmax*gxar[kk]) ;
14707        ss = sinf(bmax*gxar[kk]) ;
14708        if( gsiz < 2.0f ){
14709          bxar[kk] = 2.2f*cc ; byar[kk] = 2.2f*ss ;
14710        } else {
14711          bxar[kk] =  cc*bx + ss*by ;
14712          byar[kk] = -ss*bx + cc*by ;
14713        }
14714      }
14715    }
14716    mri_free(gxim) ;
14717 
14718    blim = mri_streakize( im , bxim , byim ) ;  /* do the streaking */
14719 
14720    mri_free(bxim) ; mri_free(byim) ; mri_free(im) ;
14721 
14722    im = mri_sharpen_rgb(0.666f,blim) ; mri_free(blim) ; /* 26 Sep 2017 */
14723    return im ;
14724 }
14725