home *** CD-ROM | disk | FTP | other *** search
Text File | 1997-05-01 | 8.8 KB | 217 lines | [TEXT/CWIE] |
- // This may look like C code, but it is really -*- C++ -*-
- /*
- ************************************************************************
- *
- * Constructing of an Elevation Map
- *
- * This code implements the ElevationMap class, the brunt of which is
- * construction of the map: generating it at random (as fractal clouds), or
- * loading from a givem file.
- *
- * The file to load an ElevationMap from must be a PGM file, with the pixel matrix
- * in its data fork. PGM, a Portable Graymap format, is fairly popular on UNIX
- * platform: a variety of tools/converters/picture editors can create/read/and
- * save pictures in this format. On a Mac, a PGM file can be created by an NIH Image
- * application: the famous GraphicConverter can make PGM out of almost any other
- * picture format (PICT, GIF, TIFF, etc) into PGM. Since PGM is a portable format, one
- * can use a PGM file created using any other tool on any other platform. Thus
- * limiting the data file format to PGM is not that big a deal.
- *
- * The PGM file to load an ElevationMap from must have a Mac type 'PPGM',
- * which is what GraphicConverter assigns when it saves a file in this format.
- *
- * The PGM is the best format to store an elevation map in: indeed, map's
- * pixels are "elevation codes" related to elevations of the corresponding points
- * on the map (see "ElevationMap.h" for more detail). The elevation codes and
- * grayscale pixel values (luminance) share the same property: the bigger the value, the
- * higher the corresponding point on a map is, or the brighter the corresponding pixel
- * is. Therefore, it is very natural to use grayscale pixels to represent elevation maps.
- *
- * To associate colors with elevation codes, a user may supply a colormap, as
- * a 'clut' resource. The map loader would try to load the first clut resource
- * it can find in the input (PGM) file. One can always use ResEdit to add a resource
- * fork to any file and add/create a 'clut' resource. This colormap should associate
- * a color to each possible elevation code: thus, the colormap must have 256 entries
- * (otherwise, it won't be loaded). The colormap should be arranged to permit color
- * interpolation (see "ElevationMap.h")
- *
- * One could argue that PICT appears to be the best format to load the ElevationMap
- * from. It is not the case, unfortunately. Although a PICT file (Picture) does have
- * a pixmap and colormap inside it, they are not directly accessible. For example, the only
- * portable and _recommended_ way to find out Picture colors is to query it via
- * GetPictInfo(). However, the colormap it returns is *not* a colormap stored within
- * the Picture object. The PictUtil package code scans the Picture and builds a new
- * colormap, which may differ from the Picture's own. For example, suppose the Picture's
- * own colormap contains an entry (value=127, color.rgb = 0xffff:0:0); further suppose
- * that the pixel with value 127 never actually occurs in the Picture's pixmap. Then
- * CLUT returned by GetPictInfo() would not have this color. Thus drawing the Picture
- * almost certainly would involve color translation, or at least pixel value remapping.
- * Although pixel value remapping is a transparent operation visually, this is a disaster
- * for us: we need pixel values themselves (because they are elevation codes), not pixel
- * colors.
- *
- * $Id$
- *
- ************************************************************************
- */
-
- #include "ElevationMap.h"
- #include "fractal_map.h"
- #include "filter.h"
- //#include <PictUtils.h>
-
- class GaussClouds : public FractalMap
- {
- public:
- GaussClouds(const card order)
- : FractalMap(order) {}
- // Well-known Gaussian random number generator
- // Note rand() returns a number [0,2^15-1],
- // that is, within [0,1] with a 15-bit implicit
- // fraction
- inline int get_noise(const card scale) const {
- long sum = 0;
- for(register int i=0; i<12; i++)
- sum += rand(); // keep the result within
- return (scale * (sum-(6<<15)))>>17; } // [-scale/2,scale/2]
- };
-
-
- // Make a default elevation map
- ElevationMap::ElevationMap(const int order)
- : IMAGE(FractalMap(order)),
- clu_table(default_clut_id),
- z_cutoff(0), elevation_factor(700)
- {
- assert(elevation_factor>10);
- filter(*this).nondet_average(30);
- }
- // Dump the contents of ElevationMap
- void ElevationMap::dump(const char title[]) const
- {
- message("\nElevation map: %s",title);
- info();
- clu_table.info();
- message(" color-to-elevation transform: cutoff %d, scale %d\n",z_cutoff,elevation_factor);
- }
-
- static inline card min(card a, card b) { return a < b ? a : b; }
-
- // assign src to an existing dst, wrapping-around if necessary
- // The source and destination IMAGEs' dimensions
- // don't have to be the same: we cut those parts of the
- // picture that don't fit into the IMAGE; we fill in the
- // parts which are shorter. In filling in, we assume
- // the PICTure is periodic...
- static void assign_wrap(IMAGE& dest, IMAGE& src)
- {
- if( dest.q_nrows() == src.q_nrows() && dest.q_ncols() == src.q_ncols() )
- {
- dest = src;
- return;
- }
-
- const int min_row = ::min(dest.q_nrows(),src.q_nrows());
- register int starting_col = 0;
- for(; starting_col + src.q_ncols() < dest.q_ncols(); starting_col += src.q_ncols())
- { // Tiling the src image (columnwise) to fill the entire dst
- dest.rectangle(rowcol(0,starting_col),
- rowcol(min_row-1,starting_col+src.q_ncols()-1)) =
- src.rectangle(rowcol(0,0),rowcol(min_row-1,src.q_ncols()-1));
- }
- dest.rectangle(rowcol(0,starting_col),
- rowcol(min_row-1,dest.q_ncols()-1)) =
- src.rectangle(rowcol(0,0),rowcol(min_row-1,dest.q_ncols()-starting_col-1));
-
- // There is overlapping assignments going on in here...
- // Still, it is done in the _right_ way, so the result
- // is correct!
- if( dest.q_nrows() > src.q_nrows() )
- dest.rectangle(rowcol(src.q_nrows(),0),rowcol(dest.q_nrows()-1,dest.q_ncols()-1)) =
- dest.rectangle(rowcol(0,0),rowcol(dest.q_nrows() - src.q_nrows()-1,dest.q_ncols()-1));
- }
-
- class FileContainer
- {
- short vRefNum; // Volume Ref number, or a Working Dir Number
- long dirID; // Dir number
- public:
- FileContainer(void) // The current file container
- { do_well( HGetVol(nil,&vRefNum,&dirID) ); }
- FileContainer(const FSSpec& file_spec) // File container of a specific file
- : vRefNum(file_spec.vRefNum), dirID(file_spec.parID) {}
- void make_current(void) const
- { do_well( HSetVol("\p",vRefNum,dirID) ); }
- void dump(const char title []) const
- { message("\nFile container '%s': volume id %d, dir id %d\n",title,vRefNum,dirID); }
- };
-
- // Try to load a CLUtable from the first clut resource of
- // the given file, and assign it to the given CLUTable,
- // if compatible (that is, has the same number of colors)
- // Return true if the assignment succeeds, return false
- // if the file does not have a clut resource, or it is
- // incompatible with the given CLUTable
- static bool load_new_clut(CLUTable& clut, const FSSpec& file_spec)
- {
- //const short prev_res_file = CurrResFile();
- const short clut_file_refno = FSpOpenResFile(&file_spec,fsRdPerm);
-
- if( clut_file_refno == -1 )
- {
- const OSErr resource_fork_open_err = ResError();
- if( resource_fork_open_err == resFNotFound )
- return false; // file_spec does not have a resource fork
- do_well( resource_fork_open_err ); // crash on any affirmative error
- return false;
- }
-
- Handle clut_handle = Get1IndResource('clut', 1);
- bool result = true;
- if( clut_handle == nil )
- {
- const OSErr resource_peek_err = ResError();
- if( resource_peek_err != resNotFound )
- do_well( resource_peek_err );
- result = false; // file_spec does not have clut resources
- }
- else
- {
- short res_id;
- ResType res_type;
- Str255 name;
- GetResInfo(clut_handle, &res_id, &res_type, name);
- CLUTable loaded_clut(res_id);
- if( result = (clut.q_size() == loaded_clut.q_size()) )
- clut.deep_copy_from(loaded_clut);
- }
- //UseResFile(prev_res_file);
- CloseResFile(clut_file_refno); // this restores the previous res file and frees up
- // clut_handle if any
- return result;
- }
-
- // Load the PGM file (with a possible clut) into the elevation map
- void ElevationMap::load(const FSSpec& file_spec)
- {
- message("Loading a new elavation map from %#s",file_spec.name);
- FileContainer old_dir;
- FileContainer(file_spec).make_current();
- // IMAGE new_image(PtoCstr((Str63)file_spec.name));
- IMAGE new_image(PtoCstr( FSSpec(file_spec).name ));
- old_dir.make_current();
- assign_wrap(*this,new_image);
-
- if( load_new_clut(clu_table,file_spec) )
- message("a custom color map has been loaded\n");
-
-
- // write_pgm("coerced.pgm");
- #if 0
- PICTure picture_ext(file_spec);
- clut = picture_ext.q_clut();
- picture_ext.q_clut().dump("Picture's");
- PICTView picture = picture_ext;
- CLUTable(picture.q_clut()).dump("Offscreen's Picture");
- #endif
- }