Blame generator/pamshadedrelief.c

Packit 78deda
/*=============================================================================
Packit 78deda
                               pamshaderelief
Packit 78deda
===============================================================================
Packit 78deda
  Generate a shaded relief image of terrain, given a terrain map - a two
Packit 78deda
  dimensional map of elevations.  A shaded relief image is an image of
Packit 78deda
  what terrain with the given elevations would look like illuminated by
Packit 78deda
  oblique light.
Packit 78deda
Packit 78deda
  The input array is a one-channel PAM image.  The sample values are
Packit 78deda
  elevations of terrain.
Packit 78deda
  
Packit 78deda
  This is derived from John Walker's 'pgmcrater' which not only does this
Packit 78deda
  shading, but first generates a terrain map of fractal craters on which to
Packit 78deda
  run it.
Packit 78deda
Packit 78deda
Packit 78deda
  The original program carried this attribution and license:
Packit 78deda
Packit 78deda
       Designed and implemented in November of 1989 by:
Packit 78deda
Packit 78deda
        John Walker
Packit 78deda
        Autodesk SA
Packit 78deda
        Avenue des Champs-Montants 14b
Packit 78deda
        CH-2074 MARIN
Packit 78deda
        Switzerland
Packit 78deda
        Usenet: kelvin@Autodesk.com
Packit 78deda
        Fax:    038/33 88 15
Packit 78deda
        Voice:  038/33 76 33
Packit 78deda
Packit 78deda
  Permission  to  use, copy, modify, and distribute this software and
Packit 78deda
  its documentation  for  any  purpose  and  without  fee  is  hereby
Packit 78deda
  granted,  without any conditions or restrictions.  This software is
Packit 78deda
  provided "as is" without express or implied warranty.
Packit 78deda
Packit 78deda
=============================================================================*/
Packit 78deda
Packit 78deda
/* Modifications by Arjen Bax, 2001-06-21: Remove black vertical line at right
Packit 78deda
   edge.
Packit 78deda
*/
Packit 78deda
Packit 78deda
#define _XOPEN_SOURCE 500  /* get M_PI in math.h */
Packit 78deda
Packit 78deda
#include <assert.h>
Packit 78deda
#include <math.h>
Packit 78deda
Packit 78deda
#include "pm_c_util.h"
Packit 78deda
#include "mallocvar.h"
Packit 78deda
#include "nstring.h"
Packit 78deda
#include "shhopt.h"
Packit 78deda
#include "pam.h"
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
struct CmdlineInfo {
Packit 78deda
    /* All the information the user supplied in the command line,
Packit 78deda
       in a form easy for the program to use.
Packit 78deda
    */
Packit 78deda
    const char * inputFileName;
Packit 78deda
    float        gamma;
Packit 78deda
};
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
parseCommandLine(int argc, const char ** const argv,
Packit 78deda
                 struct CmdlineInfo * const cmdlineP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Note that the file spec array we return is stored in the storage that
Packit 78deda
   was passed to us as the argv array.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    optEntry * option_def;
Packit 78deda
        /* Instructions to OptParseOptions3 on how to parse our options.
Packit 78deda
         */
Packit 78deda
    optStruct3 opt;
Packit 78deda
    unsigned int option_def_index;
Packit 78deda
Packit 78deda
    unsigned int gammaSpec;
Packit 78deda
Packit 78deda
    MALLOCARRAY_NOFAIL(option_def, 100);
Packit 78deda
Packit 78deda
    option_def_index = 0;   /* incremented by OPTENT3 */
Packit 78deda
    OPTENT3(0,   "gamma",    OPT_FLOAT,   &cmdlineP->gamma,
Packit 78deda
            &gammaSpec,       0);
Packit 78deda
Packit 78deda
    opt.opt_table = option_def;
Packit 78deda
    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
Packit 78deda
    opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
Packit 78deda
Packit 78deda
    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
Packit 78deda
        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
Packit 78deda
Packit 78deda
    if (!gammaSpec)
Packit 78deda
        cmdlineP->gamma = 1.0;
Packit 78deda
Packit 78deda
    if (cmdlineP->gamma <= 0.0)
Packit 78deda
        pm_error("gamma correction must be greater than 0");
Packit 78deda
Packit 78deda
    if (argc-1 == 0) 
Packit 78deda
        cmdlineP->inputFileName = "-";
Packit 78deda
    else if (argc-1 != 1)
Packit 78deda
        pm_error("Program takes zero or one argument (filename).  You "
Packit 78deda
                 "specified %u", argc-1);
Packit 78deda
    else
Packit 78deda
        cmdlineP->inputFileName = argv[1];
Packit 78deda
Packit 78deda
    free(option_def);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
/* Definitions for obtaining random numbers. */
Packit 78deda
Packit 78deda
/*  Display parameters  */
Packit 78deda
Packit 78deda
static double const ImageGamma = 0.5;     /* Inherent gamma of mapped image */
Packit 78deda
static int    const slopemin   = -52;
Packit 78deda
static int    const slopemax   = 52;
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
generateSlopeGrayMap(sample * const slopeGrayMap,
Packit 78deda
                     double   const dgamma) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Map each possible slope to the brightness that terrain with that
Packit 78deda
   left-to-right slope should have in the shaded relief.
Packit 78deda
Packit 78deda
   The brightness is what would result from light incident from the left
Packit 78deda
   falling on the terrain.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    double const gamma = dgamma * ImageGamma;
Packit 78deda
Packit 78deda
    int i;
Packit 78deda
Packit 78deda
    for (i = slopemin; i <= 0; ++i) {   /* Negative, downhill, dark */
Packit 78deda
        slopeGrayMap[i - slopemin] =
Packit 78deda
            128 - 127.0 * pow(sin((M_PI / 2) * i / slopemin), gamma);
Packit 78deda
    }
Packit 78deda
    for (i = 0; i <= slopemax; ++i) {   /* Positive, uphill, bright */
Packit 78deda
        slopeGrayMap[i - slopemin] =
Packit 78deda
            128 + 127.0 * pow(sin((M_PI / 2) * i / slopemax), gamma);
Packit 78deda
    }
Packit 78deda
Packit 78deda
    /* Confused?   OK,  we're using the  left-to-right slope to
Packit 78deda
       calculate a shade based on the sine of  the  angle  with
Packit 78deda
       respect  to the vertical (light incident from the left).
Packit 78deda
       Then, with one exponentiation, we account for  both  the
Packit 78deda
       inherent   gamma   of   the   image  (ad-hoc),  and  the
Packit 78deda
       user-specified display gamma, using the identity:
Packit 78deda
       (x^y)^z = (x^(y*z))
Packit 78deda
    */
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static gray
Packit 78deda
brightnessOfSlope(int      const slope,
Packit 78deda
                  sample * const slopeGrayMap) {
Packit 78deda
Packit 78deda
    return slopeGrayMap[MIN(MAX(slopemin, slope), slopemax) - slopemin];
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
writeShadedRelief(struct pam * const terrainPamP,
Packit 78deda
                  tuple **     const terrain,
Packit 78deda
                  double       const dgamma,
Packit 78deda
                  FILE *       const ofP) {
Packit 78deda
Packit 78deda
    unsigned int row;
Packit 78deda
    tuple * outrow;
Packit 78deda
    sample * slopeGrayMap; /* Slope to gray value map */
Packit 78deda
    struct pam outpam;
Packit 78deda
Packit 78deda
    outpam.size   = sizeof(outpam);
Packit 78deda
    outpam.len    = PAM_STRUCT_SIZE(tuple_type);
Packit 78deda
    outpam.file   = ofP;
Packit 78deda
    outpam.format = PAM_FORMAT;
Packit 78deda
    outpam.height = terrainPamP->height;
Packit 78deda
    outpam.width  = terrainPamP->width;
Packit 78deda
    outpam.depth  = 1;
Packit 78deda
    outpam.maxval = 255;
Packit 78deda
    outpam.bytes_per_sample = 1;
Packit 78deda
    STRSCPY(outpam.tuple_type, "GRAYSCALE");
Packit 78deda
Packit 78deda
    outrow = pnm_allocpamrow(&outpam);
Packit 78deda
Packit 78deda
    pnm_writepaminit(&outpam);
Packit 78deda
Packit 78deda
    MALLOCARRAY(slopeGrayMap, slopemax - slopemin + 1);
Packit 78deda
Packit 78deda
    generateSlopeGrayMap(slopeGrayMap, dgamma);
Packit 78deda
Packit 78deda
    for (row = 0; row < terrainPamP->height; ++row) {
Packit 78deda
        unsigned int col;
Packit 78deda
Packit 78deda
        for (col = 0; col < terrainPamP->width - 1; ++col) {
Packit 78deda
            int const slope = terrain[row][col+1][0] - terrain[row][col][0];
Packit 78deda
            outrow[col][0] = brightnessOfSlope(slope, slopeGrayMap);
Packit 78deda
        }
Packit 78deda
        {
Packit 78deda
            /* Wrap around to determine shade of pixel on right edge */
Packit 78deda
            int const slope = 
Packit 78deda
                terrain[row][0][0] - terrain[row][outpam.width-1][0];
Packit 78deda
            outrow[outpam.width - 1][0] =
Packit 78deda
                brightnessOfSlope(slope, slopeGrayMap);
Packit 78deda
        }
Packit 78deda
        pnm_writepamrow(&outpam, outrow);
Packit 78deda
    }
Packit 78deda
Packit 78deda
    free(slopeGrayMap);
Packit 78deda
    pnm_freepamrow(outrow);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
readTerrain(FILE *       const ifP,
Packit 78deda
            struct pam * const pamP,
Packit 78deda
            tuple ***    const tuplesP) {
Packit 78deda
Packit 78deda
    *tuplesP = pnm_readpam(ifP, pamP, PAM_STRUCT_SIZE(tuple_type));
Packit 78deda
}
Packit 78deda
            
Packit 78deda
            
Packit 78deda
Packit 78deda
int
Packit 78deda
main(int argc, const char ** argv) {
Packit 78deda
Packit 78deda
    struct CmdlineInfo cmdline;
Packit 78deda
    FILE * ifP;
Packit 78deda
    struct pam terrainPam;
Packit 78deda
    tuple ** terrain;
Packit 78deda
        /* Array of elevations */
Packit 78deda
Packit 78deda
    pm_proginit(&argc, argv);
Packit 78deda
Packit 78deda
    parseCommandLine(argc, argv, &cmdline);
Packit 78deda
Packit 78deda
    ifP = pm_openr(cmdline.inputFileName);
Packit 78deda
Packit 78deda
    readTerrain(ifP, &terrainPam, &terrain);
Packit 78deda
Packit 78deda
    writeShadedRelief(&terrainPam, terrain, cmdline.gamma, stdout);
Packit 78deda
    
Packit 78deda
    return 0;
Packit 78deda
}
Packit 78deda
Packit 78deda