1 /**
2  * \file Reader for MGH/MGZ (FreeSurfer) format files.
3  */
4 
5 #ifdef HAVE_CONFIG_H
6 #include "config.h"
7 #endif /*HAVE_CONFIG_H*/
8 
9 #include "input_mgh.h"
10 
11 #ifdef _WIN32
12 #  define WIN32_LEAN_AND_MEAN
13 #  include <Winsock2.h>
14 #else
15 #  include <arpa/inet.h> /* for ntohl and ntohs */
16 #endif
17 #include "znzlib.h"
18 #include "errno.h"
19 
20 #define NUM_BYTE_VALUES      (UCHAR_MAX + 1)
21 
22 #define MGH_MAX_DIMS 4          /* Maximum number of dimensions */
23 #define MGH_N_SPATIAL VIO_N_DIMENSIONS /* Number of spatial dimensions */
24 #define MGH_N_COMPONENTS 4      /* Number of transform components. */
25 #define MGH_N_XFORM (MGH_N_COMPONENTS * MGH_N_SPATIAL)
26 
27 #define MGH_HEADER_SIZE 284 /* Total number of bytes in the header. */
28 
29 #define MGH_EXTRA_SIZE 194   /* Number of "unused" bytes in the header. */
30 
31 #define MGH_TYPE_UCHAR 0  /**< Voxels are 1-byte unsigned integers. */
32 #define MGH_TYPE_INT 1    /**< Voxels are 4-byte signed integers. */
33 #define MGH_TYPE_LONG 2   /**< Unsupported here.  */
34 #define MGH_TYPE_FLOAT 3  /**< Voxels are 4-byte floating point. */
35 #define MGH_TYPE_SHORT 4  /**< Voxels are 2-byte signed integers. */
36 #define MGH_TYPE_BITMAP 5 /**< Unsupported here. */
37 #define MGH_TYPE_TENSOR 6 /**< Unsupported here. */
38 
39 /* MGH tag types, at least the ones that are minimally documented.
40  */
41 #define TAG_OLD_COLORTABLE          1
42 #define TAG_OLD_USEREALRAS          2
43 #define TAG_CMDLINE                 3
44 #define TAG_USEREALRAS              4
45 #define TAG_COLORTABLE              5
46 
47 #define TAG_GCAMORPH_GEOM           10
48 #define TAG_GCAMORPH_TYPE           11
49 #define TAG_GCAMORPH_LABELS         12
50 
51 #define TAG_OLD_SURF_GEOM           20
52 #define TAG_SURF_GEOM               21
53 
54 #define TAG_OLD_MGH_XFORM           30
55 #define TAG_MGH_XFORM               31
56 #define TAG_GROUP_AVG_SURFACE_AREA  32
57 
58 /**
59  * Information in the MGH/MGZ file header.
60  */
61 struct mgh_header {
62   int version;                  /**< Must be 0x00000001. */
63   int sizes[MGH_MAX_DIMS];      /**< Dimension sizes, fastest-varying FIRST. */
64   int type;                     /**< One of the MGH_TYPE_xxx values. */
65   int dof;                      /**< Degrees of freedom, if used. */
66   short goodRASflag;            /**< True if spacing and dircos valid. */
67   float spacing[MGH_N_SPATIAL]; /**< Dimension spacing.  */
68   float dircos[MGH_N_COMPONENTS][MGH_N_SPATIAL]; /**< Dimension transform. */
69 };
70 
71 /**
72  * Trailer information found immediately AFTER the hyperslab of data.
73  */
74 struct mgh_trailer {
75   float TR;
76   float FlipAngle;
77   float TE;
78   float TI;
79   float FoV;
80 };
81 
82 /**
83  * Trivial function to swap floats if necessary.
84  * \param f The big-endian 4-byte float value.
85  * \return The float value in host byte order.
86  */
87 static float
swapFloat(float f)88 swapFloat(float f)
89 {
90   union {
91     float f;
92     int i;
93   } sl;
94 
95   sl.f = f;
96 
97   sl.i = ntohl(sl.i);
98 
99   return sl.f;
100 }
101 
102 /**
103  * Reads the next slice from the MGH volume. As a side effect, it advances
104  * the slice_index value in the volume input structure if successful.
105  *
106  * \param in_ptr The volume input information.
107  * \return VIO_OK if success
108  */
109 static VIO_Status
input_next_slice(volume_input_struct * in_ptr)110 input_next_slice(
111                  volume_input_struct *in_ptr
112                  )
113 {
114   size_t n_voxels_in_slice;
115   size_t n_bytes_per_voxel;
116   znzFile fp = (znzFile) in_ptr->volume_file;
117 
118   n_bytes_per_voxel = get_type_size(in_ptr->file_data_type);
119   n_voxels_in_slice = (in_ptr->sizes_in_file[0] *
120                        in_ptr->sizes_in_file[1]);
121   if (znzread(in_ptr->generic_slice_buffer,
122               n_bytes_per_voxel,
123               n_voxels_in_slice,
124               fp) != n_voxels_in_slice)
125   {
126     fprintf(stderr, "read error %d\n", errno);
127     return VIO_ERROR;
128   }
129 
130   return VIO_OK;
131 }
132 
133 /**
134  * Converts a MGH file header into a general linear transform for the
135  * Volume IO library. There are two different ways of defining the
136  " "centre" of the volume in the MGH world. One uses the values in
137  * c_r, c_a, and c_s (the last row of the dircos field) to offset
138  * the origin. The other, more common case ignores these fields and
139  * just uses the voxel size and spacing to determine a value for
140  * the centre.
141  * Note that the geometric structures produced by MGH tools use
142  * the latter case.
143  *
144  * \param hdr_ptr A pointer to the MGH header structure.
145  * \param in_ptr A pointer to the input information for this volume.
146  * \param ignore_offsets TRUE if we should use grid centres.
147  * \param linear_xform_ptr A pointer to the output transform
148  */
149 static void
mgh_header_to_linear_transform(const struct mgh_header * hdr_ptr,const volume_input_struct * in_ptr,VIO_BOOL ignore_offsets,struct VIO_General_transform * linear_xform_ptr)150 mgh_header_to_linear_transform(const struct mgh_header *hdr_ptr,
151                                const volume_input_struct *in_ptr,
152                                VIO_BOOL ignore_offsets,
153                                struct VIO_General_transform *linear_xform_ptr)
154 {
155   int           i, j;
156   VIO_Transform mnc_xform;
157   VIO_Real      mgh_xform[MGH_N_SPATIAL][MGH_N_COMPONENTS];
158 
159   make_identity_transform(&mnc_xform);
160 
161 #if DEBUG
162   /* Print out the raw MGH transform information.
163    */
164   for (i = 0; i < MGH_N_SPATIAL; i++)
165   {
166     for (j = 0; j < MGH_N_COMPONENTS; j++)
167     {
168       printf("%c_%c %8.4f ", "xyzc"[j], "ras"[i], hdr_ptr->dircos[j][i]);
169     }
170     printf("\n");
171   }
172 #endif // DEBUG
173 
174   /* Multiply the direction cosines by the spacings.
175    */
176   for (i = 0; i < MGH_N_SPATIAL; i++)
177   {
178     for (j = 0; j < MGH_N_SPATIAL; j++)
179     {
180       mgh_xform[i][j] = hdr_ptr->dircos[j][i] * hdr_ptr->spacing[i];
181     }
182   }
183 
184   /* Work out the final MGH transform. This requires that we figure out
185    * the origin values to fill in the final column of the transform.
186    */
187   for (i = 0; i < MGH_N_SPATIAL; i++)
188   {
189     double temp = 0.0;
190     for (j = 0; j < MGH_N_SPATIAL; j++)
191     {
192       temp += mgh_xform[i][j] * (hdr_ptr->sizes[j] / 2.0);
193     }
194 
195     /* Set the origin for the voxel-to-world transform .
196      */
197     if (ignore_offsets)
198     {
199       mgh_xform[i][MGH_N_COMPONENTS - 1] = -temp;
200     }
201     else
202     {
203       mgh_xform[i][MGH_N_COMPONENTS - 1] = hdr_ptr->dircos[MGH_N_COMPONENTS - 1][i] - temp;
204     }
205   }
206 
207 #if DEBUG
208   printf("mgh_xform:\n");       /* DEBUG */
209   for (i = 0; i < MGH_N_SPATIAL; i++)
210   {
211     for (j = 0; j < MGH_N_COMPONENTS; j++)
212     {
213       printf("%.4f ", mgh_xform[i][j]);
214     }
215     printf("\n");
216   }
217 #endif // DEBUG
218 
219   /* Convert MGH transform to the MINC format. The only difference is
220    * that our transform is always written in XYZ (RAS) order, so we
221    * have to swap the _columns_ as needed.
222    */
223   for (i = 0; i < MGH_N_SPATIAL; i++)
224   {
225     for (j = 0; j < MGH_N_COMPONENTS; j++)
226     {
227       int volume_axis = j;
228       if (j < VIO_N_DIMENSIONS)
229       {
230         volume_axis = in_ptr->axis_index_from_file[j];
231       }
232       Transform_elem(mnc_xform, i, volume_axis) = mgh_xform[i][j];
233     }
234   }
235   create_linear_transform(linear_xform_ptr, &mnc_xform);
236 #if DEBUG
237   output_transform(stdout, "debug", NULL, NULL, linear_xform_ptr);
238 #endif // DEBUG
239 }
240 
241 /**
242  * Read an MGH header from an open file stream.
243  * \param fp The open file (may be a pipe).
244  * \param hdr_ptr A pointer to the header structure to fill in.
245  * \returns TRUE if successful.
246  */
247 static VIO_BOOL
mgh_header_from_file(znzFile fp,struct mgh_header * hdr_ptr)248 mgh_header_from_file(znzFile fp, struct mgh_header *hdr_ptr)
249 {
250   int i, j;
251   char dummy[MGH_HEADER_SIZE];
252 
253   /* Read in the header. We do this piecemeal because the mgh_header
254    * struct will not be properly aligned on most systems, so a single
255    * fread() WILL NOT WORK.
256    */
257   if (znzread(&hdr_ptr->version, sizeof(int), 1, fp) != 1 ||
258       znzread(hdr_ptr->sizes, sizeof(int), MGH_MAX_DIMS, fp) != MGH_MAX_DIMS ||
259       znzread(&hdr_ptr->type, sizeof(int), 1, fp) != 1 ||
260       znzread(&hdr_ptr->dof, sizeof(int), 1, fp) != 1 ||
261       znzread(&hdr_ptr->goodRASflag, sizeof(short), 1, fp) != 1 ||
262       /* The rest of the fields are optional, but we can safely read them
263        * now and check goodRASflag later to see if we should really trust
264        * them.
265        */
266       znzread(hdr_ptr->spacing, sizeof(float), MGH_N_SPATIAL, fp) != MGH_N_SPATIAL ||
267       znzread(hdr_ptr->dircos, sizeof(float), MGH_N_XFORM, fp) != MGH_N_XFORM ||
268       znzread(dummy, 1, MGH_EXTRA_SIZE, fp) != MGH_EXTRA_SIZE)
269   {
270     print_error("Problem reading MGH file header.");
271     return FALSE;
272   }
273 
274   /* Successfully read all of the data. Now we have to convert it to the
275    * machine's byte ordering.
276    */
277   hdr_ptr->version = ntohl(hdr_ptr->version);
278   for (i = 0; i < MGH_MAX_DIMS; i++)
279   {
280     hdr_ptr->sizes[i] = ntohl(hdr_ptr->sizes[i]);
281   }
282   hdr_ptr->type = ntohl(hdr_ptr->type);
283   hdr_ptr->dof = ntohl(hdr_ptr->dof);
284   hdr_ptr->goodRASflag = ntohs(hdr_ptr->goodRASflag);
285 
286   if (hdr_ptr->version != 1)
287   {
288     print_error("Must be MGH version 1.\n");
289     return FALSE;
290   }
291 
292   if (hdr_ptr->goodRASflag)
293   {
294     for (i = 0; i < MGH_N_SPATIAL; i++)
295     {
296       hdr_ptr->spacing[i] = swapFloat(hdr_ptr->spacing[i]);
297       for (j = 0; j < MGH_N_COMPONENTS; j++)
298       {
299         hdr_ptr->dircos[j][i] = swapFloat(hdr_ptr->dircos[j][i]);
300       }
301     }
302   }
303   else
304   {
305     /* Flag is zero, so just use the defaults.
306      */
307     for (i = 0; i < MGH_N_SPATIAL; i++)
308     {
309       /* Default spacing is 1.0.
310        */
311       hdr_ptr->spacing[i] = 1.0;
312 
313       /* Assume coronal orientation.
314        */
315       for (j = 0; j < MGH_N_COMPONENTS; j++)
316       {
317         hdr_ptr->dircos[j][i] = 0.0;
318       }
319       hdr_ptr->dircos[0][0] = -1.0;
320       hdr_ptr->dircos[1][2] = -1.0;
321       hdr_ptr->dircos[2][1] = 1.0;
322     }
323   }
324   return TRUE;
325 }
326 
327 static VIO_BOOL
mgh_scan_for_voxel_range(volume_input_struct * in_ptr,long n_voxels_in_slice,float * min_value_ptr,float * max_value_ptr)328 mgh_scan_for_voxel_range(volume_input_struct *in_ptr,
329                          long n_voxels_in_slice,
330                          float *min_value_ptr,
331                          float *max_value_ptr)
332 {
333   znzFile fp = (znzFile) in_ptr->volume_file;
334   float min_value = FLT_MAX;
335   float max_value = -FLT_MAX;
336   int slice;
337   float value = 0;
338   int i;
339   void *data_ptr;
340   long int data_offset = znztell((znzFile) fp);
341   int total_slices = in_ptr->sizes_in_file[2];
342 
343   if (in_ptr->sizes_in_file[3] > 1)
344   {
345     /* If there is a time dimension, incorporate that into our slice
346      * count.
347      */
348     total_slices *= in_ptr->sizes_in_file[3];
349   }
350 
351   if (data_offset < 0)
352     return FALSE;
353 
354   for (slice = 0; slice < total_slices; slice++)
355   {
356     input_next_slice( in_ptr );
357     data_ptr = in_ptr->generic_slice_buffer;
358     for (i = 0; i < n_voxels_in_slice; i++)
359     {
360       switch (in_ptr->file_data_type)
361       {
362       case VIO_UNSIGNED_BYTE:
363         value = ((unsigned char *)data_ptr)[i];
364         break;
365 
366       case VIO_SIGNED_SHORT:
367         value = ntohs(((short *)data_ptr)[i]);
368         break;
369 
370       case VIO_SIGNED_INT:
371         value = ntohl(((int *)data_ptr)[i]);
372         break;
373 
374       case VIO_FLOAT:
375         value = swapFloat(((float *)data_ptr)[i]);
376         break;
377 
378       case VIO_NO_DATA_TYPE:
379       case VIO_SIGNED_BYTE:
380       case VIO_UNSIGNED_SHORT:
381       case VIO_UNSIGNED_INT:
382       case VIO_DOUBLE:
383       case VIO_MAX_DATA_TYPE:
384         break;
385       }
386 
387       if (value < min_value )
388         min_value = value;
389       if (value > max_value )
390         max_value = value;
391     }
392   }
393 
394   *min_value_ptr = min_value;
395   *max_value_ptr = max_value;
396   in_ptr->slice_index = 0;
397   znzseek((znzFile) fp, data_offset, SEEK_SET);
398   return TRUE;
399 }
400 
401 VIOAPI  VIO_Status
initialize_mgh_format_input(VIO_STR filename,VIO_Volume volume,volume_input_struct * in_ptr)402 initialize_mgh_format_input(VIO_STR             filename,
403                             VIO_Volume          volume,
404                             volume_input_struct *in_ptr)
405 {
406   int               sizes[VIO_MAX_DIMENSIONS];
407   long              n_voxels_in_slice;
408   int               n_bytes_per_voxel;
409   nc_type           desired_nc_type;
410   znzFile           fp;
411   int               axis;
412   struct mgh_header hdr;
413   VIO_General_transform mnc_native_xform;
414 
415   VIO_Real          mnc_dircos[VIO_N_DIMENSIONS][VIO_N_DIMENSIONS];
416   VIO_Real          mnc_steps[VIO_MAX_DIMENSIONS];
417   VIO_Real          mnc_starts[VIO_MAX_DIMENSIONS];
418   int               n_dimensions;
419   nc_type           file_nc_type;
420   VIO_BOOL          signed_flag;
421 
422   if ((fp = znzopen(filename, "rb", 1)) == NULL)
423   {
424     print_error("Unable to open file %s, errno %d.\n", filename, errno);
425     return VIO_ERROR;
426   }
427 
428   if (!mgh_header_from_file(fp, &hdr))
429   {
430     znzclose(fp);
431     return VIO_ERROR;
432   }
433 
434   /* Translate from MGH to VIO types.
435    */
436   switch (hdr.type)
437   {
438   case MGH_TYPE_UCHAR:
439     in_ptr->file_data_type = VIO_UNSIGNED_BYTE;
440     file_nc_type = NC_BYTE;
441     signed_flag = FALSE;
442     break;
443   case MGH_TYPE_INT:
444     in_ptr->file_data_type = VIO_SIGNED_INT;
445     file_nc_type = NC_INT;
446     signed_flag = TRUE;
447     break;
448   case MGH_TYPE_FLOAT:
449     in_ptr->file_data_type = VIO_FLOAT;
450     file_nc_type = NC_FLOAT;
451     signed_flag = TRUE;
452     break;
453   case MGH_TYPE_SHORT:
454     in_ptr->file_data_type = VIO_SIGNED_SHORT;
455     file_nc_type = NC_SHORT;
456     signed_flag = TRUE;
457     break;
458   default:
459     print_error("Unknown MGH data type.\n");
460     znzclose(fp);
461     return VIO_ERROR;
462   }
463 
464   /* Decide how to store data in memory. */
465 
466   if ( get_volume_data_type(volume) == VIO_NO_DATA_TYPE )
467     desired_nc_type = file_nc_type;
468   else
469     desired_nc_type = get_volume_nc_data_type(volume, &signed_flag);
470 
471   if( volume->spatial_axes[VIO_X] < 0 ||
472       volume->spatial_axes[VIO_Y] < 0 ||
473       volume->spatial_axes[VIO_Z] < 0 )
474   {
475     print_error("warning: setting MGH spatial axes to XYZ.\n");
476     volume->spatial_axes[VIO_X] = 0;
477     volume->spatial_axes[VIO_Y] = 1;
478     volume->spatial_axes[VIO_Z] = 2;
479   }
480 
481   /* Calculate the number of non-trivial dimensions in the file.
482    */
483   n_dimensions = 0;
484   for_less( axis, 0, MGH_MAX_DIMS )
485   {
486     in_ptr->sizes_in_file[axis] = hdr.sizes[axis];
487     if (hdr.sizes[axis] > 1)
488     {
489       n_dimensions++;
490     }
491   }
492 
493   if (!set_volume_n_dimensions(volume, n_dimensions))
494   {
495     printf("Problem setting number of dimensions to %d\n", n_dimensions);
496   }
497 
498   /* Set up the correspondence between the file axes and the MINC
499    * spatial axes. Each row contains the 'x', 'y', and 'z' components
500    * along the right/left, anterior/posterior, or superior/inferior
501    * axes (RAS). The "xspace" axis will be the one that has the largest
502    * component in the RL direction, "yspace" refers to AP, and "zspace"
503    * to SI. This tells us both how to convert the transform and how the
504    * file data is arranged.
505    */
506   for_less( axis, 0, MGH_N_SPATIAL )
507   {
508     int spatial_axis = VIO_X;
509     float c_x = fabsf(hdr.dircos[axis][VIO_X]);
510     float c_y = fabsf(hdr.dircos[axis][VIO_Y]);
511     float c_z = fabsf(hdr.dircos[axis][VIO_Z]);
512 
513     if (c_y > c_x && c_y > c_z)
514     {
515       spatial_axis = VIO_Y;
516     }
517     if (c_z > c_x && c_z > c_y)
518     {
519       spatial_axis = VIO_Z;
520     }
521     in_ptr->axis_index_from_file[axis] = spatial_axis;
522   }
523 
524   /* Record the time axis, if present.
525    */
526   in_ptr->axis_index_from_file[3] = 3;
527 
528   mgh_header_to_linear_transform(&hdr, in_ptr, TRUE, &mnc_native_xform);
529 
530   convert_transform_to_starts_and_steps(&mnc_native_xform,
531                                         VIO_N_DIMENSIONS,
532                                         NULL,
533                                         volume->spatial_axes,
534                                         mnc_starts,
535                                         mnc_steps,
536                                         mnc_dircos);
537 
538   delete_general_transform(&mnc_native_xform);
539 
540   for_less( axis, 0, VIO_N_DIMENSIONS)
541   {
542     int volume_axis = volume->spatial_axes[axis];
543     int file_axis = in_ptr->axis_index_from_file[volume_axis];
544     sizes[file_axis] = in_ptr->sizes_in_file[volume_axis];
545     set_volume_direction_cosine(volume, volume_axis, mnc_dircos[volume_axis]);
546   }
547 #if DEBUG
548   for_less( axis, 0, VIO_N_DIMENSIONS)
549   {
550     int volume_axis = volume->spatial_axes[axis];
551 
552     printf("%d %d size:%4d step:%6.3f start:%9.4f dc:[%7.4f %7.4f %7.4f]\n",
553            axis,
554            in_ptr->axis_index_from_file[volume_axis],
555            sizes[volume_axis],
556            mnc_steps[volume_axis],
557            mnc_starts[volume_axis],
558            mnc_dircos[volume_axis][0],
559            mnc_dircos[volume_axis][1],
560            mnc_dircos[volume_axis][2]);
561   }
562 #endif // DEBUG
563   /* If there is a time axis, just assign a default step size of one.
564    */
565   mnc_steps[3] = 1;
566   set_volume_separations( volume, mnc_steps );
567 
568   /* If there is a time axis, just assign a default start time of zero.
569    */
570   mnc_starts[3] = 0;
571   set_volume_starts( volume, mnc_starts );
572 
573   set_volume_type( volume, desired_nc_type, signed_flag, 0.0, 0.0 );
574 
575   /* If we are a 4D image, we need to copy the size here.
576    */
577   sizes[3] = in_ptr->sizes_in_file[3];
578   set_volume_sizes( volume, sizes );
579 
580   n_bytes_per_voxel = get_type_size( in_ptr->file_data_type );
581 
582   n_voxels_in_slice = (in_ptr->sizes_in_file[0] *
583                        in_ptr->sizes_in_file[1]);
584 
585   in_ptr->min_value = DBL_MAX;
586   in_ptr->max_value = -DBL_MAX;
587 
588   /* Allocate the slice buffer. */
589 
590   in_ptr->generic_slice_buffer = malloc(n_voxels_in_slice * n_bytes_per_voxel);
591   if (in_ptr->generic_slice_buffer == NULL)
592   {
593     return VIO_ERROR;
594   }
595 
596   in_ptr->volume_file = (FILE *) fp;
597 
598   in_ptr->slice_index = 0;
599 
600   /* If the data must be converted to byte, read the entire image file simply
601    * to find the max and min values. This allows us to set the value_scale and
602    * value_translation properly when we read the file.
603    */
604   if (get_volume_data_type(volume) != in_ptr->file_data_type )
605   {
606     float min_value, max_value;
607 
608     mgh_scan_for_voxel_range(in_ptr, n_voxels_in_slice, &min_value, &max_value);
609     set_volume_voxel_range(volume, min_value, max_value);
610   }
611   return VIO_OK;
612 }
613 
614 VIOAPI void
delete_mgh_format_input(volume_input_struct * in_ptr)615 delete_mgh_format_input(
616                         volume_input_struct   *in_ptr
617                         )
618 {
619   znzFile fp = (znzFile) in_ptr->volume_file;
620 
621   free( in_ptr->generic_slice_buffer );
622 
623   znzclose( fp );
624 }
625 
626 VIOAPI  VIO_BOOL
input_more_mgh_format_file(VIO_Volume volume,volume_input_struct * in_ptr,VIO_Real * fraction_done)627 input_more_mgh_format_file(
628                            VIO_Volume          volume,
629                            volume_input_struct *in_ptr,
630                            VIO_Real            *fraction_done
631                            )
632 {
633   int            i;
634   VIO_Real       value = 0;
635   VIO_Status     status;
636   VIO_Real       value_translation, value_scale;
637   VIO_Real       original_min_voxel, original_max_voxel;
638   int            *inner_index, indices[VIO_MAX_DIMENSIONS];
639   void           *data_ptr;
640   int            data_ind;
641   int            total_slices = in_ptr->sizes_in_file[2];
642 
643   if (in_ptr->sizes_in_file[3] > 1)
644   {
645     /* If there is a time dimension, incorporate that into our slice
646      * count.
647      */
648     total_slices *= in_ptr->sizes_in_file[3];
649   }
650 
651   if ( in_ptr->slice_index < total_slices )
652   {
653     /* If the memory for the volume has not been allocated yet,
654      * initialize that memory now.
655      */
656     if (!volume_is_alloced(volume))
657     {
658       alloc_volume_data(volume);
659       if (!volume_is_alloced(volume))
660       {
661         print_error("Failed to allocate volume.\n");
662         return FALSE;
663       }
664     }
665 
666     status = input_next_slice( in_ptr );
667     if ( status != VIO_OK )
668     {
669         return FALSE;
670     }
671 
672     /* See if we need to apply scaling to this slice. This is only
673      * needed if the volume voxel type is not the same as the file
674      * voxel type. THIS IS ONLY REALLY LEGAL FOR BYTE VOLUME TYPES.
675      */
676     if (get_volume_data_type(volume) != in_ptr->file_data_type)
677     {
678       get_volume_voxel_range(volume, &original_min_voxel, &original_max_voxel);
679       value_translation = original_min_voxel;
680       value_scale = (original_max_voxel - original_min_voxel) /
681         (VIO_Real) (NUM_BYTE_VALUES - 1);
682     }
683     else
684     {
685       /* Just do the trivial scaling. */
686       value_translation = 0.0;
687       value_scale = 1.0;
688     }
689 
690     /* Set up the indices.
691      */
692     inner_index = &indices[in_ptr->axis_index_from_file[0]];
693 
694     if (in_ptr->sizes_in_file[3] > 1)
695     {
696       /* If a time dimension is present, convert the slice index into
697        * both a time and slice coordinate using the number of slices.
698        */
699       indices[in_ptr->axis_index_from_file[3]] = in_ptr->slice_index / in_ptr->sizes_in_file[2];
700       indices[in_ptr->axis_index_from_file[2]] = in_ptr->slice_index % in_ptr->sizes_in_file[2];
701     }
702     else
703     {
704       indices[in_ptr->axis_index_from_file[2]] = in_ptr->slice_index;
705     }
706 
707     if ( status == VIO_OK )
708     {
709       data_ptr = in_ptr->generic_slice_buffer;
710       data_ind = 0;
711 
712       for_less( i, 0, in_ptr->sizes_in_file[1] )
713       {
714         indices[in_ptr->axis_index_from_file[1]] = i;
715         for_less( *inner_index, 0, in_ptr->sizes_in_file[0] )
716         {
717           switch ( in_ptr->file_data_type )
718           {
719           case VIO_UNSIGNED_BYTE:
720             value = ((unsigned char *)data_ptr)[data_ind++];
721             break;
722           case VIO_SIGNED_SHORT:
723             value = ntohs(((short *)data_ptr)[data_ind++]);
724             break;
725           case VIO_SIGNED_INT:
726             value = ntohl(((int *)data_ptr)[data_ind++]);
727             break;
728           case VIO_FLOAT:
729             value = swapFloat(((float *)data_ptr)[data_ind++]);
730             break;
731           default:
732             handle_internal_error( "input_more_mgh_format_file" );
733             break;
734           }
735           value = (value - value_translation) / value_scale;
736           if (value > in_ptr->max_value)
737           {
738             in_ptr->max_value = value;
739           }
740           if (value < in_ptr->min_value)
741           {
742             in_ptr->min_value = value;
743           }
744           set_volume_voxel_value( volume,
745                                   indices[VIO_X],
746                                   indices[VIO_Y],
747                                   indices[VIO_Z],
748                                   indices[3],
749                                   0,
750                                   value);
751         }
752       }
753     }
754     in_ptr->slice_index++;      /* Advance to the next slice. */
755   }
756 
757   *fraction_done = (VIO_Real) in_ptr->slice_index / total_slices;
758 
759   /* See if we are all done. If so, we need to perform a final check
760    * of the volume to set the ranges appropriately.
761    */
762   if (in_ptr->slice_index == total_slices)
763   {
764     set_volume_voxel_range( volume, in_ptr->min_value, in_ptr->max_value );
765 
766     /* Make sure we scale the data up to the original real range,
767      * if appropriate.
768      */
769     if (get_volume_data_type(volume) != in_ptr->file_data_type)
770     {
771       set_volume_real_range(volume, original_min_voxel, original_max_voxel);
772     }
773 
774     return FALSE;
775   }
776   else
777   {
778     return TRUE;                /* More work to do. */
779   }
780 }
781