/* * vp_file.c * * Routines for loading and storing volume data in disk files. * * Copyright (c) 1994 The Board of Trustees of The Leland Stanford * Junior University. All rights reserved. * * Permission to use, copy, modify and distribute this software and its * documentation for any purpose is hereby granted without fee, provided * that the above copyright notice and this permission notice appear in * all copies of this software and that you do not sell the software. * Commercial licensing is available by contacting the author. * * THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND, * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. * * Author: * Phil Lacroute * Computer Systems Laboratory * Electrical Engineering Dept. * Stanford University */ /* * $Date$ * $Revision$ */ #include "vp_global.h" static int StoreRLEVoxels ANSI_ARGS((vpContext *vpc, int fd, RLEVoxels *rle_voxels)); static int LoadRLEVoxels ANSI_ARGS((vpContext *vpc, int fd, RLEVoxels *rle_voxels, int offsets, int swab)); static void SwapWords ANSI_ARGS((void *data, unsigned size)); static void SwapVoxels ANSI_ARGS((vpContext *vpc, void *voxels, int num_voxels, int fields, int bytes_per_voxel)); #ifdef DEBUG void VPCheckScanOffsets ANSI_ARGS((RLEVoxels *rle_voxels, int rle_bytes_per_voxel)); #endif static void SwapOctreeNode ANSI_ARGS((vpContext *vpc, int level, void *node)); static int StoreTable ANSI_ARGS((vpContext *vpc, int fd, float *ptr, unsigned size)); static int LoadTable ANSI_ARGS((vpContext *vpc, int fd, float **ptr_ptr, unsigned *size_ptr)); /******************************************************************* * Classified Volume Files. * *******************************************************************/ /* file header structure */ typedef struct { unsigned magic; /* magic number for identification */ unsigned xlen; /* voxels in each dimension */ unsigned ylen; unsigned zlen; unsigned bytes_per_voxel; /* size of a classified voxel */ unsigned num_shade_fields; /* number of fields in a classified voxel (not including opacity) */ unsigned num_x_runs; /* number of run lengths for X view */ unsigned num_x_voxels; /* number of nonzero voxels for X view */ unsigned num_x_offsets; /* number of offsets per slice for X view */ unsigned num_y_runs; /* number of run lengths for Y view */ unsigned num_y_voxels; /* number of nonzero voxels for Y view */ unsigned num_y_offsets; /* number of offsets per slice for Y view */ unsigned num_z_runs; /* number of run lengths for Z view */ unsigned num_z_voxels; /* number of nonzero voxels for Z view */ unsigned num_z_offsets; /* number of offsets per slice for Z view */ float min_opacity; /* low opacity threshold */ } RLEVoxelHdr; /* * File layout: * RLEVoxelHdr hdr; * unsigned field_size[hdr.num_shade_fields]; (size of each voxel field) * unsigned field_offset[hdr.num_shade_fields]; (offset for each field) * unsigned field_max[hdr.num_shade_fields]; (max. value of each field) * padding to align to double word * unsigned char x_run_lengths[hdr.num_x_runs]; (run lengths for X view) * padding to align to double word * char x_data[hdr.num_x_voxels*hdr.bytes_per_voxel]; (voxel data for X view) * padding to align to double word * ScanOffset x_offsets[hdr.num_x_offsets]; (scanline offset for X view) * padding to align to double word * unsigned char y_run_lengths[hdr.num_y_runs]; (run lengths for Y view) * padding to align to double word * char y_data[hdr.num_y_voxels*hdr.bytes_per_voxel]; (voxel data for Y view) * padding to align to double word * ScanOffset y_offsets[hdr.num_y_offsets]; (scanline offset for Y view) * padding to align to double word * unsigned char z_run_lengths[hdr.num_z_runs]; (run lengths for Z view) * padding to align to double word * char z_data[hdr.num_z_voxels*hdr.bytes_per_voxel]; (voxel data for Z view) * padding to align to double word * ScanOffset z_offsets[hdr.num_z_offsets]; (scanline offset for Z view) * * The padding ensures that voxel data can be mapped into memory * without any word alignment problems. */ /* * vpStoreClassifiedVolume * * Store a run-length encoded, classified volume to a file. */ vpResult vpStoreClassifiedVolume(vpc, fd) vpContext *vpc; /* context containing the volume */ int fd; /* UNIX file descriptor open for writing */ { RLEVoxelHdr header; unsigned field_data[3*VP_MAX_FIELDS]; int nsf, c; unsigned size; char pad_data[8]; int pad_bytes; int retcode; /* check for errors */ if ((retcode = VPCheckVoxelFields(vpc)) != VP_OK) return(retcode); /* write header */ header.magic = VP_CVFILE_MAGIC; header.xlen = vpc->xlen; header.ylen = vpc->ylen; header.zlen = vpc->zlen; header.bytes_per_voxel = vpc->rle_bytes_per_voxel; header.num_shade_fields = vpc->num_shade_fields; if (vpc->rle_x == NULL) { header.num_x_runs = 0; header.num_x_voxels = 0; header.num_x_offsets = 0; } else { if ((retcode = VPCheckClassifiedVolume(vpc, VP_X_AXIS)) != VP_OK) return(retcode); header.num_x_runs = vpc->rle_x->run_count; header.num_x_voxels = vpc->rle_x->data_count; header.num_x_offsets = vpc->rle_x->scan_offsets_per_slice; } if (vpc->rle_y == NULL) { header.num_y_runs = 0; header.num_y_voxels = 0; header.num_y_offsets = 0; } else { if ((retcode = VPCheckClassifiedVolume(vpc, VP_Y_AXIS)) != VP_OK) return(retcode); header.num_y_runs = vpc->rle_y->run_count; header.num_y_voxels = vpc->rle_y->data_count; header.num_y_offsets = vpc->rle_y->scan_offsets_per_slice; } if (vpc->rle_z == NULL) { header.num_z_runs = 0; header.num_z_voxels = 0; header.num_z_offsets = 0; } else { if ((retcode = VPCheckClassifiedVolume(vpc, VP_Z_AXIS)) != VP_OK) return(retcode); header.num_z_runs = vpc->rle_z->run_count; header.num_z_voxels = vpc->rle_z->data_count; header.num_z_offsets = vpc->rle_z->scan_offsets_per_slice; } header.min_opacity = vpc->min_opacity; if (vpc->write_func(fd, &header, sizeof(header)) != sizeof(header)) return(VPSetError(vpc, VPERROR_IO)); /* write voxel layout information */ nsf = vpc->num_shade_fields; for (c = 0; c < nsf; c++) { field_data[c] = vpc->field_size[c]; field_data[nsf + c] = vpc->field_offset[c]; field_data[2*nsf + c] = vpc->field_max[c]; } size = 3*nsf*sizeof(unsigned); if (vpc->write_func(fd, field_data, size) != size) return(VPSetError(vpc, VPERROR_IO)); /* padding after header */ pad_bytes = (8 - ((sizeof(header) + size) % 8)) & 0x7; if (pad_bytes > 0) { bzero(pad_data, pad_bytes); if (vpc->write_func(fd, pad_data, pad_bytes) != pad_bytes) return(VPSetError(vpc, VPERROR_IO)); } /* write data */ if (vpc->rle_x != NULL) { if ((c = StoreRLEVoxels(vpc, fd, vpc->rle_x)) != VP_OK) return(c); } if (vpc->rle_y != NULL) { if ((c = StoreRLEVoxels(vpc, fd, vpc->rle_y)) != VP_OK) return(c); } if (vpc->rle_z != NULL) { if ((c = StoreRLEVoxels(vpc, fd, vpc->rle_z)) != VP_OK) return(c); } return(VP_OK); } /* * StoreRLEVoxels * * Write an RLEVoxels structure to a file. */ static int StoreRLEVoxels(vpc, fd, rle_voxels) vpContext *vpc; int fd; RLEVoxels *rle_voxels; { int size; char pad_data[8]; int pad_bytes; bzero(pad_data, sizeof(pad_data)); if (rle_voxels->run_count > 0) { size = rle_voxels->run_count; if (vpc->write_func(fd, rle_voxels->run_lengths, size) != size) return(VPSetError(vpc, VPERROR_IO)); pad_bytes = (8 - (size % 8)) & 0x7; if (pad_bytes > 0) { if (vpc->write_func(fd, pad_data, pad_bytes) != pad_bytes) return(VPSetError(vpc, VPERROR_IO)); } } if (rle_voxels->data_count > 0) { size = rle_voxels->data_count * vpc->rle_bytes_per_voxel; if (vpc->write_func(fd, rle_voxels->data, size) != size) return(VPSetError(vpc, VPERROR_IO)); pad_bytes = (8 - (size % 8)) & 0x7; if (pad_bytes > 0) { if (vpc->write_func(fd, pad_data, pad_bytes) != pad_bytes) return(VPSetError(vpc, VPERROR_IO)); } } if (rle_voxels->scan_offsets_per_slice > 0) { size = rle_voxels->scan_offsets_per_slice * rle_voxels->klen * sizeof(ScanOffset); if (vpc->write_func(fd, rle_voxels->scan_offsets, size) != size) return(VPSetError(vpc, VPERROR_IO)); pad_bytes = (8 - (size % 8)) & 0x7; if (pad_bytes > 0) { if (vpc->write_func(fd, pad_data, pad_bytes) != pad_bytes) return(VPSetError(vpc, VPERROR_IO)); } } return(VP_OK); } /* * vpLoadClassifiedVolume * * Load a run-length encoded, classified volume from a file. */ vpResult vpLoadClassifiedVolume(vpc, fd) vpContext *vpc; /* context to store the volume into */ int fd; /* UNIX file descriptor open for reading */ { RLEVoxelHdr header; unsigned field_data[3*VP_MAX_FIELDS]; int nsf, c, swab; unsigned size; unsigned char *data; char pad_data[8]; int pad_bytes; unsigned x_run_offset; unsigned x_data_offset; unsigned x_offset_offset; unsigned y_run_offset; unsigned y_data_offset; unsigned y_offset_offset; unsigned z_run_offset; unsigned z_data_offset; unsigned z_offset_offset; int current_offset; int destroy_old_volume; /* read header */ if (vpc->read_func(fd, &header, sizeof(header)) != sizeof(header)) return(VPSetError(vpc, VPERROR_IO)); swab = 0; if (header.magic != VP_CVFILE_MAGIC) { SwapWords(&header, sizeof(header)); if (header.magic != VP_CVFILE_MAGIC) return(VPSetError(vpc, VPERROR_BAD_FILE)); swab = 1; } /* read voxel layout information */ size = 3 * header.num_shade_fields * sizeof(unsigned); if (vpc->read_func(fd, field_data, size) != size) return(VPSetError(vpc, VPERROR_IO)); if (swab) SwapWords(field_data, size); /* padding after header */ pad_bytes = (8 - ((sizeof(header) + size) % 8)) & 0x7; if (pad_bytes > 0) { if (vpc->read_func(fd, pad_data, pad_bytes) != pad_bytes) return(VPSetError(vpc, VPERROR_IO)); } /* check for consistency with old volume data */ destroy_old_volume = 0; if (vpc->xlen != header.xlen || vpc->ylen != header.ylen || vpc->zlen != header.zlen || vpc->raw_bytes_per_voxel < header.bytes_per_voxel || vpc->num_voxel_fields < header.num_shade_fields) destroy_old_volume = 1; nsf = header.num_shade_fields; for (c = 0; c < nsf; c++) { if (vpc->field_size[c] != field_data[c] || vpc->field_offset[c] != field_data[nsf + c] || vpc->field_max[c] != field_data[2*nsf + c]) destroy_old_volume = 1; } if (destroy_old_volume) { vpDestroyClassifiedVolume(vpc); vpDestroyMinMaxOctree(vpc); vpc->raw_voxels = NULL; vpc->raw_voxels_size = 0; vpc->xstride = 0; vpc->ystride = 0; vpc->zstride = 0; } /* load new volume size */ if (destroy_old_volume) { vpc->xlen = header.xlen; vpc->ylen = header.ylen; vpc->zlen = header.zlen; vpc->raw_bytes_per_voxel = header.bytes_per_voxel; nsf = header.num_shade_fields; vpc->num_voxel_fields = nsf; for (c = 0; c < nsf; c++) { vpc->field_size[c] = field_data[c]; vpc->field_offset[c] = field_data[nsf + c]; vpc->field_max[c] = field_data[2*nsf + c]; } } vpc->num_shade_fields = nsf; vpc->min_opacity = header.min_opacity; vpc->rle_bytes_per_voxel = header.bytes_per_voxel; /* load new volume data */ if (vpc->mmap_func != NULL && !swab) { /* compute file offsets */ current_offset = sizeof(header) + size; current_offset += (8 - (current_offset % 8)) & 0x7; x_run_offset = current_offset; current_offset += header.num_x_runs; current_offset += (8 - (current_offset % 8)) & 0x7; x_data_offset = current_offset; current_offset += header.num_x_voxels * header.bytes_per_voxel; current_offset += (8 - (current_offset % 8)) & 0x7; x_offset_offset = current_offset; current_offset += header.num_x_offsets * sizeof(ScanOffset); current_offset += (8 - (current_offset % 8)) & 0x7; y_run_offset = current_offset; current_offset += header.num_y_runs; current_offset += (8 - (current_offset % 8)) & 0x7; y_data_offset = current_offset; current_offset += header.num_y_voxels * header.bytes_per_voxel; current_offset += (8 - (current_offset % 8)) & 0x7; y_offset_offset = current_offset; current_offset += header.num_y_offsets * sizeof(ScanOffset); current_offset += (8 - (current_offset % 8)) & 0x7; z_run_offset = current_offset; current_offset += header.num_z_runs; current_offset += (8 - (current_offset % 8)) & 0x7; z_data_offset = current_offset; current_offset += header.num_z_voxels * header.bytes_per_voxel; current_offset += (8 - (current_offset % 8)) & 0x7; z_offset_offset = current_offset; current_offset += header.num_z_offsets * sizeof(ScanOffset); /* memory-map the data */ if ((data = vpc->mmap_func(fd, current_offset, vpc->client_data)) == NULL) return(VPSetError(vpc, VPERROR_IO)); /* assign pointers to x view data */ vpc->rle_x = VPCreateRLEVoxels(vpc, header.ylen, header.zlen, header.xlen, 0, 0, 0); vpc->rle_x->run_count = header.num_x_runs; if (header.num_x_runs > 0) vpc->rle_x->run_lengths = (unsigned char *)(data + x_run_offset); vpc->rle_x->data_count = header.num_x_voxels; if (header.num_x_voxels > 0) vpc->rle_x->data = (void *)(data + x_data_offset); vpc->rle_x->scan_offsets_per_slice = header.num_x_offsets; if (header.num_x_offsets > 0) vpc->rle_x->scan_offsets = (ScanOffset *)(data + x_offset_offset); vpc->rle_x->mmapped = 1; /* assign pointers to y view data */ vpc->rle_y = VPCreateRLEVoxels(vpc, header.zlen, header.xlen, header.ylen, 0, 0, 0); vpc->rle_y->run_count = header.num_y_runs; if (header.num_y_runs > 0) vpc->rle_y->run_lengths = (unsigned char *)(data + y_run_offset); vpc->rle_y->data_count = header.num_y_voxels; if (header.num_y_voxels > 0) vpc->rle_y->data = (void *)(data + y_data_offset); vpc->rle_y->scan_offsets_per_slice = header.num_y_offsets; if (header.num_y_offsets > 0) vpc->rle_y->scan_offsets = (ScanOffset *)(data + y_offset_offset); vpc->rle_y->mmapped = 1; /* assign pointers to z view data */ vpc->rle_z = VPCreateRLEVoxels(vpc, header.xlen, header.ylen, header.zlen, 0, 0, 0); vpc->rle_z->run_count = header.num_z_runs; if (header.num_z_runs > 0) vpc->rle_z->run_lengths = (unsigned char *)(data + z_run_offset); vpc->rle_z->data_count = header.num_z_voxels; if (header.num_z_voxels > 0) vpc->rle_z->data = (void *)(data + z_data_offset); vpc->rle_z->scan_offsets_per_slice = header.num_z_offsets; if (header.num_z_offsets > 0) vpc->rle_z->scan_offsets = (ScanOffset *)(data + z_offset_offset); vpc->rle_z->mmapped = 1; } else { /* read the x view data into memory */ if (header.num_x_runs != 0) { vpc->rle_x = VPCreateRLEVoxels(vpc, header.ylen, header.zlen, header.xlen, header.num_x_voxels, header.num_x_runs, header.bytes_per_voxel); if ((c = LoadRLEVoxels(vpc, fd, vpc->rle_x, header.num_x_offsets, swab)) != VP_OK) return(c); } /* read the y view data into memory */ if (header.num_y_runs != 0) { vpc->rle_y = VPCreateRLEVoxels(vpc, header.zlen, header.xlen, header.ylen, header.num_y_voxels, header.num_y_runs, header.bytes_per_voxel); if ((c = LoadRLEVoxels(vpc, fd, vpc->rle_y, header.num_y_offsets, swab)) != VP_OK) return(c); } /* read the z view data into memory */ if (header.num_z_runs != 0) { vpc->rle_z = VPCreateRLEVoxels(vpc, header.xlen, header.ylen, header.zlen, header.num_z_voxels, header.num_z_runs, header.bytes_per_voxel); if ((c = LoadRLEVoxels(vpc, fd, vpc->rle_z, header.num_z_offsets, swab)) != VP_OK) return(c); } } #ifdef DEBUG if (vpc->rle_x != NULL) { printf("Checking X scanline offsets....\n"); VPCheckScanOffsets(vpc->rle_x, vpc->rle_bytes_per_voxel); } if (vpc->rle_y != NULL) { printf("Checking Y scanline offsets....\n"); VPCheckScanOffsets(vpc->rle_y, vpc->rle_bytes_per_voxel); } if (vpc->rle_z != NULL) { printf("Checking Z scanline offsets....\n"); VPCheckScanOffsets(vpc->rle_z, vpc->rle_bytes_per_voxel); } #endif return(VP_OK); } /* * LoadRLEVoxels * * Load an RLEVoxels structure from a file. */ static int LoadRLEVoxels(vpc, fd, rle_voxels, offsets, swab) vpContext *vpc; int fd; RLEVoxels *rle_voxels; int offsets; int swab; { int size; char pad_data[8]; int pad_bytes; if (rle_voxels->run_count > 0) { size = rle_voxels->run_count; if (vpc->read_func(fd, rle_voxels->run_lengths, size) != size) return(VPSetError(vpc, VPERROR_IO)); pad_bytes = (8 - (size % 8)) & 0x7; if (pad_bytes > 0) { if (vpc->read_func(fd, pad_data, pad_bytes) != pad_bytes) return(VPSetError(vpc, VPERROR_IO)); } } if (rle_voxels->data_count > 0) { size = rle_voxels->data_count * vpc->rle_bytes_per_voxel; if (vpc->read_func(fd, rle_voxels->data, size) != size) return(VPSetError(vpc, VPERROR_IO)); if (swab) SwapVoxels(vpc, rle_voxels->data, rle_voxels->data_count, vpc->num_shade_fields, vpc->rle_bytes_per_voxel); pad_bytes = (8 - (size % 8)) & 0x7; if (pad_bytes > 0) { if (vpc->read_func(fd, pad_data, pad_bytes) != pad_bytes) return(VPSetError(vpc, VPERROR_IO)); } } if (offsets > 0) { rle_voxels->scan_offsets_per_slice = offsets; size = rle_voxels->klen * offsets * sizeof(ScanOffset); Alloc(vpc, rle_voxels->scan_offsets, ScanOffset *, size, "scan_offsets"); if (vpc->read_func(fd, rle_voxels->scan_offsets, size) != size) return(VPSetError(vpc, VPERROR_IO)); if (swab) SwapWords(rle_voxels->scan_offsets, size); pad_bytes = (8 - (size % 8)) & 0x7; if (pad_bytes > 0) { if (vpc->read_func(fd, pad_data, pad_bytes) != pad_bytes) return(VPSetError(vpc, VPERROR_IO)); } } return(VP_OK); } /* * SwapWords * * Byte-swap word data to change the endianess. */ static void SwapWords(data, size) void *data; unsigned size; { unsigned char *ptr; int tmp1, tmp2; ptr = data; while (size >= 4) { tmp1 = ptr[0]; ptr[0] = ptr[3]; ptr[3] = tmp1; tmp2 = ptr[1]; ptr[1] = ptr[2]; ptr[2] = tmp2; size -= 4; ptr += 4; } } /* * SwapVoxels * * Byte-swap voxel data to change the endianess. */ static void SwapVoxels(vpc, voxels, num_voxels, fields, bytes_per_voxel) vpContext *vpc; /* context */ void *voxels; /* array of voxels */ int num_voxels; /* number of voxels in the array */ int fields; /* number of fields in voxel */ int bytes_per_voxel; /* size of voxel in bytes */ { int f, size, offset; unsigned char *voxel_ptr; int tmp1, tmp2; /* check if any of the fields of the voxel need swapping */ size = 0; for (f = 0; f < fields; f++) { if (vpc->field_size[f] > size) size = vpc->field_size[f]; } if (size <= 1) return; /* do the swapping */ voxel_ptr = voxels; while (num_voxels-- > 0) { for (f = 0; f < fields; f++) { size = vpc->field_size[f]; if (size == 1) continue; offset = vpc->field_offset[f]; if (size == 2) { tmp1 = voxel_ptr[offset]; voxel_ptr[offset] = voxel_ptr[offset+1]; voxel_ptr[offset+1] = tmp1; } else { tmp1 = voxel_ptr[offset]; voxel_ptr[offset] = voxel_ptr[offset+3]; voxel_ptr[offset+3] = tmp1; tmp2 = voxel_ptr[offset+1]; voxel_ptr[offset+1] = voxel_ptr[offset+2]; voxel_ptr[offset+2] = tmp2; } } voxel_ptr += bytes_per_voxel; } } /******************************************************************* * Min-Max Octree Files. * *******************************************************************/ /* file header structure */ typedef struct { unsigned magic; /* magic number for identification */ unsigned xlen; /* voxels in each dimension */ unsigned ylen; unsigned zlen; int num_clsfy_params; /* # of params for classification */ int levels; /* number of levels in octree */ int root_node_size; /* voxels/side for root level */ int base_node_size; /* voxels/side for base level */ int range_bytes_per_node; /* bytes/node for min/max data */ int base_bytes_per_node; /* bytes/node for base level */ int nonbase_bytes_per_node; /* bytes/node for non-base level */ int status_offset; /* offset to status field */ int child_offset; /* offset to child field */ unsigned octree_bytes; /* bytes of storage for the octree */ } MinMaxOctreeHdr; /* * File layout: * MinMaxOctreeHdr hdr; * unsigned param_size[hdr.num_clsfy_params]; (size of each parameter, bytes) * unsigned param_max[hdr.num_clsfy_params]; (max. value of each parameter) * unsigned node_offset[hdr.num_clsfy_params];(node offset to min/max data) * char data[octree_bytes]; (octree data) */ /* * vpStoreMinMaxOctree * * Store a min-max octree to a file. */ vpResult vpStoreMinMaxOctree(vpc, fd) vpContext *vpc; /* context containing the octree */ int fd; /* UNIX file descriptor open for writing */ { MinMaxOctreeHdr header; unsigned field_data[3*VP_MAX_FIELDS]; int ncp, c; unsigned size; if (vpc->mm_octree == NULL) return(VPSetError(vpc, VPERROR_BAD_SIZE)); /* write header */ bzero(&header, sizeof(MinMaxOctreeHdr)); header.magic = VP_OCTFILE_MAGIC; header.xlen = vpc->xlen; header.ylen = vpc->ylen; header.zlen = vpc->zlen; header.num_clsfy_params = vpc->num_clsfy_params; header.levels = vpc->mm_octree->levels; header.root_node_size = vpc->mm_octree->root_node_size; header.base_node_size = vpc->mm_octree->base_node_size; header.range_bytes_per_node = vpc->mm_octree->range_bytes_per_node; header.base_bytes_per_node = vpc->mm_octree->base_bytes_per_node; header.nonbase_bytes_per_node = vpc->mm_octree->nonbase_bytes_per_node; header.status_offset = vpc->mm_octree->status_offset; header.child_offset = vpc->mm_octree->child_offset; header.octree_bytes = vpc->mm_octree->octree_bytes; if (vpc->write_func(fd, &header, sizeof(header)) != sizeof(header)) return(VPSetError(vpc, VPERROR_IO)); /* write parameter size/offset information */ ncp = vpc->num_clsfy_params; for (c = 0; c < ncp; c++) { field_data[c] = vpc->field_size[vpc->param_field[c]]; field_data[ncp + c] = vpc->field_max[vpc->param_field[c]]; field_data[2*ncp + c] = vpc->mm_octree->node_offset[c]; } size = 3*ncp*sizeof(unsigned); if (vpc->write_func(fd, field_data, size) != size) return(VPSetError(vpc, VPERROR_IO)); /* write octree data */ size = vpc->mm_octree->octree_bytes; if (vpc->write_func(fd, vpc->mm_octree->root, size) != size) return(VPSetError(vpc, VPERROR_IO)); return(VP_OK); } /* * vpLoadMinMaxOctree * * Load a min-max octree from a file. */ vpResult vpLoadMinMaxOctree(vpc, fd) vpContext *vpc; /* context to store the octree into */ int fd; /* UNIX file descriptor open for reading */ { MinMaxOctreeHdr header; unsigned field_data[3*VP_MAX_FIELDS]; int ncp, c, swab; unsigned size; /* read header */ if (vpc->read_func(fd, &header, sizeof(header)) != sizeof(header)) return(VPSetError(vpc, VPERROR_IO)); swab = 0; if (header.magic != VP_OCTFILE_MAGIC) { SwapWords(&header, sizeof(header)); if (header.magic != VP_OCTFILE_MAGIC) return(VPSetError(vpc, VPERROR_BAD_FILE)); swab = 1; } /* read parameter size/offset information */ size = 3 * header.num_clsfy_params * sizeof(unsigned); if (vpc->read_func(fd, field_data, size) != size) return(VPSetError(vpc, VPERROR_IO)); if (swab) SwapWords(field_data, size); /* check for consistency with current volume data */ if ((c = VPCheckRawVolume(vpc)) != VP_OK) return(c); if (header.xlen != vpc->xlen || header.ylen != vpc->ylen || header.zlen != vpc->zlen || header.num_clsfy_params != vpc->num_clsfy_params) return(VPSetError(vpc, VPERROR_BAD_VOLUME)); ncp = vpc->num_clsfy_params; for (c = 0; c < ncp; c++) { if (field_data[c] != vpc->field_size[vpc->param_field[c]] || field_data[ncp + c] != vpc->field_max[vpc->param_field[c]]) return(VPSetError(vpc, VPERROR_BAD_VOXEL)); } /* clear old octree */ vpDestroyMinMaxOctree(vpc); /* initialize new octree */ Alloc(vpc, vpc->mm_octree, MinMaxOctree *, sizeof(MinMaxOctree), "MinMaxOctree"); bzero(vpc->mm_octree, sizeof(MinMaxOctree)); vpc->mm_octree->levels = header.levels; vpc->mm_octree->root_node_size = header.root_node_size; vpc->mm_octree->base_node_size = header.base_node_size; vpc->mm_octree->range_bytes_per_node = header.range_bytes_per_node; vpc->mm_octree->base_bytes_per_node = header.base_bytes_per_node; vpc->mm_octree->nonbase_bytes_per_node = header.nonbase_bytes_per_node; vpc->mm_octree->status_offset = header.status_offset; vpc->mm_octree->child_offset = header.child_offset; vpc->mm_octree->octree_bytes = header.octree_bytes; ncp = header.num_clsfy_params; for (c = 0; c < ncp; c++) vpc->mm_octree->node_offset[c] = field_data[2*ncp + c]; /* load octree data */ size = header.octree_bytes; Alloc(vpc, vpc->mm_octree->root, void *, size, "mm_octree"); if (vpc->read_func(fd, vpc->mm_octree->root, size) != size) return(VPSetError(vpc, VPERROR_IO)); if (swab) SwapOctreeNode(vpc, 0, vpc->mm_octree->root); return(VP_OK); } /* * SwapOctreeNode * * Recursive depth-first traversal of an octree to byte-swap each node's * data (in order to switch the endianess). */ static void SwapOctreeNode(vpc, level, node) vpContext *vpc; int level; void *node; { int p, field, size, offset, tmp1, tmp2; int child_bytes_per_node; char *node_ptr = node; /* byte swap min-max data */ for (p = 0; p < vpc->num_clsfy_params; p++) { field = vpc->param_field[p]; size = vpc->field_size[field]; if (size != 1) { ASSERT(size == 2); offset = vpc->mm_octree->node_offset[p]; tmp1 = node_ptr[offset]; node_ptr[offset] = node_ptr[offset+1]; node_ptr[offset+1] = tmp1; tmp2 = node_ptr[offset+2]; node_ptr[offset+2] = node_ptr[offset+3]; node_ptr[offset+3] = tmp2; } } /* byte swap child pointer and recurse */ if (level != vpc->mm_octree->levels-1) { offset = vpc->mm_octree->child_offset; tmp1 = node_ptr[offset]; node_ptr[offset] = node_ptr[offset+3]; node_ptr[offset+3] = tmp1; tmp2 = node_ptr[offset+1]; node_ptr[offset+1] = node_ptr[offset+2]; node_ptr[offset+2] = tmp2; ASSERT(IntField(node, offset) != 0); node_ptr = (char *)vpc->mm_octree->root + IntField(node, offset); if (level == vpc->mm_octree->levels-2) child_bytes_per_node = vpc->mm_octree->base_bytes_per_node; else child_bytes_per_node = vpc->mm_octree->nonbase_bytes_per_node; SwapOctreeNode(vpc, level+1, node_ptr); node_ptr += child_bytes_per_node; SwapOctreeNode(vpc, level+1, node_ptr); node_ptr += child_bytes_per_node; SwapOctreeNode(vpc, level+1, node_ptr); node_ptr += child_bytes_per_node; SwapOctreeNode(vpc, level+1, node_ptr); node_ptr += child_bytes_per_node; SwapOctreeNode(vpc, level+1, node_ptr); node_ptr += child_bytes_per_node; SwapOctreeNode(vpc, level+1, node_ptr); node_ptr += child_bytes_per_node; SwapOctreeNode(vpc, level+1, node_ptr); node_ptr += child_bytes_per_node; SwapOctreeNode(vpc, level+1, node_ptr); } } /******************************************************************* * Raw Volume Files. * *******************************************************************/ /* file header structure */ typedef struct { unsigned magic; /* magic number for identification */ unsigned xlen; /* voxels in each dimension */ unsigned ylen; unsigned zlen; unsigned bytes_per_voxel; /* size of a raw voxel */ unsigned num_voxel_fields; /* number of fields in a voxel */ unsigned num_shade_fields; /* number of fields for shading */ unsigned num_clsfy_fields; /* number of fields for classification */ int xstride; /* strides for voxel data */ int ystride; int zstride; } RawVoxelHdr; /* * File layout: * RawVoxelHdr hdr; * unsigned field_size[hdr.num_shade_fields]; (size of each voxel field) * unsigned field_offset[hdr.num_shade_fields]; (offset for each field) * unsigned field_max[hdr.num_shade_fields]; (max. value of each field) * char data[hdr.xlen*hdr.ylen*hdr.zlen*hdr.bytes_per_voxel]; (volume data) */ /* * vpStoreRawVolume * * Store an unclassified volume to a file. */ vpResult vpStoreRawVolume(vpc, fd) vpContext *vpc; /* context containing the volume */ int fd; /* UNIX file descriptor open for writing */ { RawVoxelHdr header; unsigned field_data[3*VP_MAX_FIELDS]; int nvf, c; unsigned size; int retcode; /* check for errors */ if ((retcode = VPCheckRawVolume(vpc)) != VP_OK) return(retcode); /* write header */ header.magic = VP_RVFILE_MAGIC; header.xlen = vpc->xlen; header.ylen = vpc->ylen; header.zlen = vpc->zlen; header.bytes_per_voxel = vpc->raw_bytes_per_voxel; header.num_voxel_fields = vpc->num_voxel_fields; header.num_shade_fields = vpc->num_shade_fields; header.num_clsfy_fields = vpc->num_clsfy_params; header.xstride = vpc->xstride; header.ystride = vpc->ystride; header.zstride = vpc->zstride; if (vpc->write_func(fd, &header, sizeof(header)) != sizeof(header)) return(VPSetError(vpc, VPERROR_IO)); /* write voxel layout information */ nvf = vpc->num_voxel_fields; for (c = 0; c < nvf; c++) { field_data[c] = vpc->field_size[c]; field_data[nvf + c] = vpc->field_offset[c]; field_data[2*nvf + c] = vpc->field_max[c]; } size = 3*nvf*sizeof(unsigned); if (vpc->write_func(fd, field_data, size) != size) return(VPSetError(vpc, VPERROR_IO)); /* write data */ if (vpc->write_func(fd, vpc->raw_voxels, vpc->raw_voxels_size) != vpc->raw_voxels_size) return(VPSetError(vpc, VPERROR_IO)); return(VP_OK); } /* * vpLoadRawVolume * * Load an unclassified volume from a file. */ vpResult vpLoadRawVolume(vpc, fd) vpContext *vpc; /* context to store the volume into */ int fd; /* UNIX file descriptor open for reading */ { RawVoxelHdr header; unsigned field_data[3*VP_MAX_FIELDS]; int nvf, c, swab; unsigned size; unsigned voxel_offset; unsigned char *data; int destroy_old_volume; /* read header */ if (vpc->read_func(fd, &header, sizeof(header)) != sizeof(header)) return(VPSetError(vpc, VPERROR_IO)); swab = 0; if (header.magic != VP_RVFILE_MAGIC) { SwapWords(&header, sizeof(header)); if (header.magic != VP_RVFILE_MAGIC) return(VPSetError(vpc, VPERROR_BAD_FILE)); swab = 1; } /* read voxel layout information */ size = 3 * header.num_voxel_fields * sizeof(unsigned); if (vpc->read_func(fd, field_data, size) != size) return(VPSetError(vpc, VPERROR_IO)); if (swab) SwapWords(field_data, size); voxel_offset = sizeof(header) + size; /* destroy old volume data */ vpDestroyClassifiedVolume(vpc); vpDestroyMinMaxOctree(vpc); /* load new volume size */ vpc->xlen = header.xlen; vpc->ylen = header.ylen; vpc->zlen = header.zlen; vpc->raw_bytes_per_voxel = header.bytes_per_voxel; vpc->num_voxel_fields = header.num_voxel_fields; vpc->num_shade_fields = header.num_shade_fields; vpc->num_clsfy_params = header.num_clsfy_fields; vpc->xstride = header.xstride; vpc->ystride = header.ystride; vpc->zstride = header.zstride; nvf = header.num_voxel_fields; for (c = 0; c < nvf; c++) { vpc->field_size[c] = field_data[c]; vpc->field_offset[c] = field_data[nvf + c]; vpc->field_max[c] = field_data[2*nvf + c]; } /* load new volume data */ size = vpc->xlen*vpc->ylen*vpc->zlen*vpc->raw_bytes_per_voxel; vpc->raw_voxels_size = size; if (vpc->mmap_func != NULL && !swab) { if ((vpc->raw_voxels = vpc->mmap_func(fd, voxel_offset, vpc->client_data)) == NULL) return(VPSetError(vpc, VPERROR_IO)); } else { Alloc(vpc, vpc->raw_voxels, void *, size, "raw_voxels"); if (vpc->read_func(fd, vpc->raw_voxels, size) != size) return(VPSetError(vpc, VPERROR_IO)); if (swab) { SwapVoxels(vpc, vpc->raw_voxels, vpc->xlen*vpc->ylen*vpc->zlen, vpc->num_voxel_fields, vpc->raw_bytes_per_voxel); } } return(VP_OK); } /******************************************************************* * Rendering Context Dump Files. * *******************************************************************/ /* file header structure */ typedef struct { unsigned magic; /* magic number for identification */ unsigned major_version; /* major version number */ unsigned minor_version; /* minor version number */ unsigned max_fields; /* value of VP_MAX_FIELDS */ unsigned max_material; /* value of VP_MAX_MATERIAL */ unsigned max_lights; /* value of VP_MAX_LIGHTS */ } VpcHdr; /* * File layout: * VpcHdr hdr; * vpContext vpc; --> truncated just before "end_of_parameters" field * unsigned shade_color_table_size; * float shade_color_table[shade_color_table_size]; * unsigned shade_weight_table_size; * float shade_weight_table[shade_weight_table_size]; * for i = 1 to vpc.num_clsfy_params: * int clsfy_table_size; * float clsfy_table[clsfy_table_size]; */ /* * vpStoreContext * * Store the contents of a volpack context to a file. All state parameters * stored directly in the vpContext structure are stored. User-supplied * lookup tables are also stored. Volume data and octrees are not stored * (use the routines specifically for storing those data structures), and * internal tables that can be computed from other state variables * (e.g. depth cueing lookup table) are not stored. */ vpResult vpStoreContext(vpc, fd) vpContext *vpc; int fd; { VpcHdr header; int i; unsigned vpc_size; header.magic = VP_VPCFILE_MAGIC; header.major_version = VP_MAJOR_VERSION; header.minor_version = VP_MINOR_VERSION; header.max_fields = VP_MAX_FIELDS; header.max_material = VP_MAX_MATERIAL; header.max_lights = VP_MAX_LIGHTS; vpc_size = vpFieldOffset(vpc, end_of_parameters); if (vpc->write_func(fd, &header, sizeof(header)) != sizeof(header)) return(VPSetError(vpc, VPERROR_IO)); if (vpc->write_func(fd, vpc, vpc_size) != vpc_size) return(VPSetError(vpc, VPERROR_IO)); if (!StoreTable(vpc, fd, vpc->shade_color_table, vpc->shade_color_table_size)) return(VPSetError(vpc, VPERROR_IO)); if (!StoreTable(vpc, fd, vpc->shade_weight_table, vpc->shade_weight_table_size)) return(VPSetError(vpc, VPERROR_IO)); for (i = 0; i < vpc->num_clsfy_params; i++) { if (!StoreTable(vpc, fd, vpc->clsfy_table[i], vpc->clsfy_table_size[i])) return(VPSetError(vpc, VPERROR_IO)); } return(VP_OK); } /* * StoreTable * * Store a table to a file and check for errors. Return value is 1 for * success, 0 for failure. */ static int StoreTable(vpc, fd, ptr, size) vpContext *vpc; int fd; float *ptr; unsigned size; { if (size == 0 || ptr == NULL) { size = 0; if (vpc->write_func(fd, &size, sizeof(size)) != sizeof(size)) return(0); } else { if (vpc->write_func(fd, &size, sizeof(size)) != sizeof(size)) return(0); if (vpc->write_func(fd, ptr, size) != size) return(0); } return(1); } /* * vpLoadContext * * Load a volpack context from a file. The old contents of the context are * destroyed, including any volume data. Lookup tables for shading and * classification that are loaded from the file are stored in newly-allocated * memory, but the application is responsible for freeing the tables; * existing tables in the context are not overwritten (since there is no * way for the application to predict the right table sizes), and the new * tables are not freed when vpDestroyContext is called (since volpack * normally does not manage the tables). Byte swapping is not performed. */ vpResult vpLoadContext(vpc, fd) vpContext *vpc; int fd; { VpcHdr header; int swab, i; unsigned vpc_size; /* read header */ if (vpc->read_func(fd, &header, sizeof(header)) != sizeof(header)) return(VPSetError(vpc, VPERROR_IO)); swab = 0; if (header.magic != VP_VPCFILE_MAGIC) return(VPSetError(vpc, VPERROR_BAD_FILE)); if (header.major_version != VP_MAJOR_VERSION || header.minor_version != VP_MINOR_VERSION || header.max_fields != VP_MAX_FIELDS || header.max_material != VP_MAX_MATERIAL || header.max_lights != VP_MAX_LIGHTS) { return(VPSetError(vpc, VPERROR_BAD_VALUE)); } /* destroy old data structures */ vpDestroyMinMaxOctree(vpc); vpDestroyClassifiedVolume(vpc); /* load new context */ vpc_size = vpFieldOffset(vpc, end_of_parameters); if (vpc->read_func(fd, vpc, vpc_size) != vpc_size) return(VPSetError(vpc, VPERROR_IO)); vpc->raw_voxels = NULL; for (i = 0; i < VP_MAX_FIELDS; i++) vpc->clsfy_table[i] = NULL; vpc->shade_color_table = NULL; vpc->shade_weight_table = NULL; vpc->image = NULL; if (vpc->shade_func == NULL) vpc->shading_mode = LOOKUP_SHADER; if (!LoadTable(vpc, fd, &vpc->shade_color_table, (unsigned *)&vpc->shade_color_table_size)) goto failed; if (!LoadTable(vpc, fd, &vpc->shade_weight_table, (unsigned *)&vpc->shade_weight_table_size)) goto failed; for (i = 0; i < vpc->num_clsfy_params; i++) { if (!LoadTable(vpc, fd, &vpc->clsfy_table[i], (unsigned *)&vpc->clsfy_table_size[i])) goto failed; } return(VP_OK); failed: if (vpc->shade_color_table != NULL) { Dealloc(vpc, vpc->shade_color_table); vpc->shade_color_table = NULL; } if (vpc->shade_weight_table != NULL) { Dealloc(vpc, vpc->shade_weight_table); vpc->shade_weight_table = NULL; } for (i = 0; i < vpc->num_clsfy_params; i++) { if (vpc->clsfy_table[i] != NULL) { Dealloc(vpc, vpc->clsfy_table[i]); vpc->clsfy_table[i] = NULL; } } return(VPSetError(vpc, VPERROR_IO)); } /* * LoadTable * * Load a table from a file and check for errors. Return value is 1 for * success, 0 for failure. */ static int LoadTable(vpc, fd, ptr_ptr, size_ptr) vpContext *vpc; int fd; float **ptr_ptr; unsigned *size_ptr; { if (vpc->read_func(fd, size_ptr, sizeof(unsigned)) != sizeof(unsigned)) return(0); if (*size_ptr != 0) { Alloc(vpc, *ptr_ptr, void *, *size_ptr, "lookup table"); if (vpc->read_func(fd, *ptr_ptr, *size_ptr) != *size_ptr) return(0); } return(1); }