I added a new mode especially for pure light sources,
such as fire against a black background. I added a
new mode for compositing a test image with your choice
of foreground, background, and alpha. I improved the
handling of tiny values, reducing the crazy colors.
There's a new -T option that sets a cut-off for what
is considered tiny; I used 29 for the palm tree.

Basic usage remains the same: make your best attempt
at editing away the foreground, do the same for the
background (weird, I know), and then feed *.ppm images
into the program. You'll get alpha as a *.pgm image.
Edit that, feed it back into the program, and you'll
get an improved foreground image. Repeat as desired.
For the final *.png file, use pnmtopng:

pnmtopng -force -alpha a.pgm f.ppm > stamp.ppm

Anybody using this tool?

/////////////////////////////////////////////////
//////////////////////////////////////////////////
///////////////////////////////////////////////////
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <math.h>
#include <float.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>

/////////////////////////////////////////////////////////////////////////////
static void usage(const char *s, int code, int err){
  if(s){
    if(err){
      perror(s);
    }else{
      fprintf(stderr, "Error: %s\n", s);
    }
  }
  fprintf(stderr, "Usage:\n"
    "  ./a.out -b background.ppm -o original.ppm -f foreground.ppm > 
alpha.pgm\n"
    "  ./a.out -b background.ppm -o original.ppm -a editedalpha.pgm -f 
foreground.ppm > newforeground.ppm\n"
    "  ./a.out -b background.ppm -o original.ppm -a editedalpha.pgm > 
newforeground.ppm\n"
    "  ./a.out -b background.ppm -f newforeground.ppm -a editedalpha.pgm > 
likeoriginal.ppm\n"
    "  ./a.out -b background.ppm -o fire-like-original.ppm > foreground.ppm\n"
  );
  exit(code);
}

/////////////////////////////////////////////////////////////////////////////

static unsigned width, height;

#define TYPE_PGM '5'
#define TYPE_PPM '6'

//////////////////////////////////////////////////////////////////////
// quick hack reader for binary P*M files
static unsigned char *readpnm(const char *name, int desired){
  int fd = open(name, O_RDONLY);
  if(fd<3) usage("fd open failure",80,1);
  char *cp;
  int type;
  struct stat sbuf;
  fstat(fd, &sbuf);
  cp = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
  if(cp==MAP_FAILED) usage("mmap failure",81,1);
  close(fd);

  if(cp[0] != 'P') exit(7);
  type = cp[1];
  if(type != TYPE_PGM && type != TYPE_PPM) exit(56);
  cp += 2;
  while(*cp=='#' || isspace(*cp)){
    if(*cp=='#') cp = strchr(cp, '\n');
    else cp++;
  }
  width = strtoul(cp, &cp, 10);
  while(*cp=='#' || isspace(*cp)){
    if(*cp=='#') cp = strchr(cp, '\n');
    else cp++;
  }
  height = strtoul(cp, &cp, 10);
  while(*cp=='#' || isspace(*cp)){
    if(*cp=='#') cp = strchr(cp, '\n');
    else cp++;
  }
  if(strtoul(cp, &cp, 10)!=255) exit(33);

  cp++;

  if(type==desired)
    return cp;
  if(type==TYPE_PPM && desired==TYPE_PGM){
    // let'd pick the red channel
    unsigned i = width*height;
    unsigned char *data = mmap(0, i, PROT_READ|PROT_WRITE, 
MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
    while(i--){
      data[i] = cp[i*3];
    }
    return data;
  }
  if(type==TYPE_PGM && desired==TYPE_PPM){
    unsigned i = width*height;
    unsigned char *data = mmap(0, i*3, PROT_READ|PROT_WRITE, 
MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
    while(i--){
      unsigned char tmp = cp[i];
      data[i*3+0] = tmp;
      data[i*3+1] = tmp;
      data[i*3+2] = tmp;
    }
    return data;
  }
  exit(93);
}


////////////////////////////////////////////////////////////////////////////////
// This goes from 8-bit sRGB (range 0 to 255) to linear (range 0 to 1).
// The math to produce a table entry:
//    tmp = oldvalue / 255.0;
//    result = (tmp<=0.03928) ? tmp/12.92 : pow((tmp+0.055)/1.055,2.4);
static const float sRGB_to_linear_table[256] = {
  0.000000, 0.000304, 0.000607, 0.000911, 0.001214, 0.001518, 0.001821,
  0.002125, 0.002428, 0.002732, 0.003035, 0.003347, 0.003677, 0.004025,
  0.004391, 0.004777, 0.005182, 0.005605, 0.006049, 0.006512, 0.006995,
  0.007499, 0.008023, 0.008568, 0.009134, 0.009721, 0.010330, 0.010960,
  0.011612, 0.012286, 0.012983, 0.013702, 0.014444, 0.015209, 0.015996,
  0.016807, 0.017642, 0.018500, 0.019382, 0.020289, 0.021219, 0.022174,
  0.023153, 0.024158, 0.025187, 0.026241, 0.027321, 0.028426, 0.029557,
  0.030713, 0.031896, 0.033105, 0.034340, 0.035601, 0.036889, 0.038204,
  0.039546, 0.040915, 0.042311, 0.043735, 0.045186, 0.046665, 0.048172,
  0.049707, 0.051269, 0.052861, 0.054480, 0.056128, 0.057805, 0.059511,
  0.061246, 0.063010, 0.064803, 0.066626, 0.068478, 0.070360, 0.072272,
  0.074214, 0.076185, 0.078187, 0.080220, 0.082283, 0.084376, 0.086500,
  0.088656, 0.090842, 0.093059, 0.095307, 0.097587, 0.099899, 0.102242,
  0.104616, 0.107023, 0.109462, 0.111932, 0.114435, 0.116971, 0.119538,
  0.122139, 0.124772, 0.127438, 0.130136, 0.132868, 0.135633, 0.138432,
  0.141263, 0.144128, 0.147027, 0.149960, 0.152926, 0.155926, 0.158961,
  0.162029, 0.165132, 0.168269, 0.171441, 0.174647, 0.177888, 0.181164,
  0.184475, 0.187821, 0.191202, 0.194618, 0.198069, 0.201556, 0.205079,
  0.208637, 0.212231, 0.215861, 0.219526, 0.223228, 0.226966, 0.230740,
  0.234551, 0.238398, 0.242281, 0.246201, 0.250158, 0.254152, 0.258183,
  0.262251, 0.266356, 0.270498, 0.274677, 0.278894, 0.283149, 0.287441,
  0.291771, 0.296138, 0.300544, 0.304987, 0.309469, 0.313989, 0.318547,
  0.323143, 0.327778, 0.332452, 0.337164, 0.341914, 0.346704, 0.351533,
  0.356400, 0.361307, 0.366253, 0.371238, 0.376262, 0.381326, 0.386429,
  0.391572, 0.396755, 0.401978, 0.407240, 0.412543, 0.417885, 0.423268,
  0.428690, 0.434154, 0.439657, 0.445201, 0.450786, 0.456411, 0.462077,
  0.467784, 0.473531, 0.479320, 0.485150, 0.491021, 0.496933, 0.502886,
  0.508881, 0.514918, 0.520996, 0.527115, 0.533276, 0.539479, 0.545724,
  0.552011, 0.558340, 0.564712, 0.571125, 0.577580, 0.584078, 0.590619,
  0.597202, 0.603827, 0.610496, 0.617207, 0.623960, 0.630757, 0.637597,
  0.644480, 0.651406, 0.658375, 0.665387, 0.672443, 0.679542, 0.686685,
  0.693872, 0.701102, 0.708376, 0.715694, 0.723055, 0.730461, 0.737910,
  0.745404, 0.752942, 0.760525, 0.768151, 0.775822, 0.783538, 0.791298,
  0.799103, 0.806952, 0.814847, 0.822786, 0.830770, 0.838799, 0.846873,
  0.854993, 0.863157, 0.871367, 0.879622, 0.887923, 0.896269, 0.904661,
  0.913099, 0.921582, 0.930111, 0.938686, 0.947307, 0.955973, 0.964686,
  0.973445, 0.982251, 0.991102, 1.000000,
};

// this goes the other way; range checking will be required
static const unsigned char linear_to_sRGB_table[4096] =
  "\x00\x01\x02\x03\x03\x04\x05\x06\x07\x08\x08\x09\x0a\x0b\x0b\x0c\x0d\x0d"
  "\x0e\x0f\x10\x10\x11\x11\x12\x12\x13\x13\x14\x14\x15\x15\x16\x16\x17\x17"
  "\x18\x18\x18\x19\x19\x1a\x1a\x1a\x1b\x1b\x1c\x1c\x1c\x1d\x1d\x1d\x1e\x1e"
  "\x1e\x1f\x1f\x1f\x20\x20\x20\x21\x21\x21\x22\x22\x22\x23\x23\x23\x23\x24"
  "\x24\x24\x25\x25\x25\x25\x26\x26\x26\x26\x27\x27\x27\x28\x28\x28\x28\x29"
  "\x29\x29\x29\x2a\x2a\x2a\x2a\x2b\x2b\x2b\x2b\x2c\x2c\x2c\x2c\x2c\x2d\x2d"
  "\x2d\x2d\x2e\x2e\x2e\x2e\x2f\x2f\x2f\x2f\x2f\x30\x30\x30\x30\x30\x31\x31"
  "\x31\x31\x31\x32\x32\x32\x32\x33\x33\x33\x33\x33\x34\x34\x34\x34\x34\x35"
  "\x35\x35\x35\x35\x35\x36\x36\x36\x36\x36\x37\x37\x37\x37\x37\x38\x38\x38"
  "\x38\x38\x38\x39\x39\x39\x39\x39\x39\x3a\x3a\x3a\x3a\x3a\x3a\x3b\x3b\x3b"
  "\x3b\x3b\x3c\x3c\x3c\x3c\x3c\x3c\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3e\x3e\x3e"
  "\x3e\x3e\x3e\x3f\x3f\x3f\x3f\x3f\x3f\x40\x40\x40\x40\x40\x40\x41\x41\x41"
  "\x41\x41\x41\x41\x42\x42\x42\x42\x42\x42\x42\x43\x43\x43\x43\x43\x43\x44"
  "\x44\x44\x44\x44\x44\x44\x45\x45\x45\x45\x45\x45\x45\x46\x46\x46\x46\x46"
  "\x46\x46\x46\x47\x47\x47\x47\x47\x47\x47\x48\x48\x48\x48\x48\x48\x48\x48"
  "\x49\x49\x49\x49\x49\x49\x49\x4a\x4a\x4a\x4a\x4a\x4a\x4a\x4a\x4b\x4b\x4b"
  "\x4b\x4b\x4b\x4b\x4c\x4c\x4c\x4c\x4c\x4c\x4c\x4c\x4d\x4d\x4d\x4d\x4d\x4d"
  "\x4d\x4d\x4e\x4e\x4e\x4e\x4e\x4e\x4e\x4e\x4f\x4f\x4f\x4f\x4f\x4f\x4f\x4f"
  "\x50\x50\x50\x50\x50\x50\x50\x50\x50\x51\x51\x51\x51\x51\x51\x51\x51\x51"
  "\x52\x52\x52\x52\x52\x52\x52\x52\x53\x53\x53\x53\x53\x53\x53\x53\x53\x54"
  "\x54\x54\x54\x54\x54\x54\x54\x54\x55\x55\x55\x55\x55\x55\x55\x55\x55\x56"
  "\x56\x56\x56\x56\x56\x56\x56\x56\x57\x57\x57\x57\x57\x57\x57\x57\x57\x58"
  "\x58\x58\x58\x58\x58\x58\x58\x58\x58\x59\x59\x59\x59\x59\x59\x59\x59\x59"
  "\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b"
  "\x5b\x5b\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5d\x5d\x5d\x5d\x5d\x5d"
  "\x5d\x5d\x5d\x5e\x5e\x5e\x5e\x5e\x5e\x5e\x5e\x5e\x5e\x5e\x5f\x5f\x5f\x5f"
  "\x5f\x5f\x5f\x5f\x5f\x5f\x60\x60\x60\x60\x60\x60\x60\x60\x60\x60\x60\x61"
  "\x61\x61\x61\x61\x61\x61\x61\x61\x61\x62\x62\x62\x62\x62\x62\x62\x62\x62"
  "\x62\x62\x63\x63\x63\x63\x63\x63\x63\x63\x63\x63\x63\x64\x64\x64\x64\x64"
  "\x64\x64\x64\x64\x64\x64\x65\x65\x65\x65\x65\x65\x65\x65\x65\x65\x65\x66"
  "\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x67\x67\x67\x67\x67\x67\x67\x67"
  "\x67\x67\x67\x67\x68\x68\x68\x68\x68\x68\x68\x68\x68\x68\x68\x69\x69\x69"
  "\x69\x69\x69\x69\x69\x69\x69\x69\x6a\x6a\x6a\x6a\x6a\x6a\x6a\x6a\x6a\x6a"
  "\x6a\x6a\x6b\x6b\x6b\x6b\x6b\x6b\x6b\x6b\x6b\x6b\x6b\x6b\x6c\x6c\x6c\x6c"
  "\x6c\x6c\x6c\x6c\x6c\x6c\x6c\x6c\x6c\x6d\x6d\x6d\x6d\x6d\x6d\x6d\x6d\x6d"
  "\x6d\x6d\x6d\x6e\x6e\x6e\x6e\x6e\x6e\x6e\x6e\x6e\x6e\x6e\x6e\x6f\x6f\x6f"
  "\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x70\x70\x70\x70\x70\x70\x70\x70\x70"
  "\x70\x70\x70\x70\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x72"
  "\x72\x72\x72\x72\x72\x72\x72\x72\x72\x72\x72\x72\x73\x73\x73\x73\x73\x73"
  "\x73\x73\x73\x73\x73\x73\x73\x74\x74\x74\x74\x74\x74\x74\x74\x74\x74\x74"
  "\x74\x74\x75\x75\x75\x75\x75\x75\x75\x75\x75\x75\x75\x75\x75\x75\x76\x76"
  "\x76\x76\x76\x76\x76\x76\x76\x76\x76\x76\x76\x77\x77\x77\x77\x77\x77\x77"
  "\x77\x77\x77\x77\x77\x77\x77\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78"
  "\x78\x78\x78\x79\x79\x79\x79\x79\x79\x79\x79\x79\x79\x79\x79\x79\x79\x7a"
  "\x7a\x7a\x7a\x7a\x7a\x7a\x7a\x7a\x7a\x7a\x7a\x7a\x7b\x7b\x7b\x7b\x7b\x7b"
  "\x7b\x7b\x7b\x7b\x7b\x7b\x7b\x7b\x7b\x7c\x7c\x7c\x7c\x7c\x7c\x7c\x7c\x7c"
  "\x7c\x7c\x7c\x7c\x7c\x7d\x7d\x7d\x7d\x7d\x7d\x7d\x7d\x7d\x7d\x7d\x7d\x7d"
  "\x7d\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7f\x7f"
  "\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x80\x80\x80\x80\x80"
  "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x81\x81\x81\x81\x81\x81\x81\x81"
  "\x81\x81\x81\x81\x81\x81\x81\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82"
  "\x82\x82\x82\x82\x83\x83\x83\x83\x83\x83\x83\x83\x83\x83\x83\x83\x83\x83"
  "\x83\x83\x84\x84\x84\x84\x84\x84\x84\x84\x84\x84\x84\x84\x84\x84\x84\x85"
  "\x85\x85\x85\x85\x85\x85\x85\x85\x85\x85\x85\x85\x85\x85\x85\x86\x86\x86"
  "\x86\x86\x86\x86\x86\x86\x86\x86\x86\x86\x86\x86\x86\x87\x87\x87\x87\x87"
  "\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x88\x88\x88\x88\x88\x88\x88"
  "\x88\x88\x88\x88\x88\x88\x88\x88\x88\x89\x89\x89\x89\x89\x89\x89\x89\x89"
  "\x89\x89\x89\x89\x89\x89\x89\x8a\x8a\x8a\x8a\x8a\x8a\x8a\x8a\x8a\x8a\x8a"
  "\x8a\x8a\x8a\x8a\x8a\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b"
  "\x8b\x8b\x8b\x8b\x8c\x8c\x8c\x8c\x8c\x8c\x8c\x8c\x8c\x8c\x8c\x8c\x8c\x8c"
  "\x8c\x8c\x8c\x8d\x8d\x8d\x8d\x8d\x8d\x8d\x8d\x8d\x8d\x8d\x8d\x8d\x8d\x8d"
  "\x8d\x8d\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e"
  "\x8e\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f"
  "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x91"
  "\x91\x91\x91\x91\x91\x91\x91\x91\x91\x91\x91\x91\x91\x91\x91\x91\x91\x92"
  "\x92\x92\x92\x92\x92\x92\x92\x92\x92\x92\x92\x92\x92\x92\x92\x92\x92\x93"
  "\x93\x93\x93\x93\x93\x93\x93\x93\x93\x93\x93\x93\x93\x93\x93\x93\x94\x94"
  "\x94\x94\x94\x94\x94\x94\x94\x94\x94\x94\x94\x94\x94\x94\x94\x94\x95\x95"
  "\x95\x95\x95\x95\x95\x95\x95\x95\x95\x95\x95\x95\x95\x95\x95\x95\x96\x96"
  "\x96\x96\x96\x96\x96\x96\x96\x96\x96\x96\x96\x96\x96\x96\x96\x96\x96\x97"
  "\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x98"
  "\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98"
  "\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99"
  "\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a"
  "\x9a\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b"
  "\x9b\x9b\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c"
  "\x9c\x9c\x9c\x9c\x9d\x9d\x9d\x9d\x9d\x9d\x9d\x9d\x9d\x9d\x9d\x9d\x9d\x9d"
  "\x9d\x9d\x9d\x9d\x9d\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e"
  "\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f"
  "\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0"
  "\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa1\xa1\xa1\xa1\xa1\xa1\xa1\xa1"
  "\xa1\xa1\xa1\xa1\xa1\xa1\xa1\xa1\xa1\xa1\xa1\xa1\xa2\xa2\xa2\xa2\xa2\xa2"
  "\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa3\xa3\xa3\xa3"
  "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa4"
  "\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4"
  "\xa4\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5"
  "\xa5\xa5\xa5\xa5\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6"
  "\xa6\xa6\xa6\xa6\xa6\xa6\xa7\xa7\xa7\xa7\xa7\xa7\xa7\xa7\xa7\xa7\xa7\xa7"
  "\xa7\xa7\xa7\xa7\xa7\xa7\xa7\xa7\xa7\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa8"
  "\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa9\xa9\xa9\xa9\xa9"
  "\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xaa\xaa"
  "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
  "\xaa\xaa\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab"
  "\xab\xab\xab\xab\xab\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac"
  "\xac\xac\xac\xac\xac\xac\xac\xac\xac\xad\xad\xad\xad\xad\xad\xad\xad\xad"
  "\xad\xad\xad\xad\xad\xad\xad\xad\xad\xad\xad\xad\xad\xae\xae\xae\xae\xae"
  "\xae\xae\xae\xae\xae\xae\xae\xae\xae\xae\xae\xae\xae\xae\xae\xae\xae\xaf"
  "\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf"
  "\xaf\xaf\xaf\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0"
  "\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
  "\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb2\xb2\xb2\xb2\xb2\xb2"
  "\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb3"
  "\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3"
  "\xb3\xb3\xb3\xb3\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4"
  "\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5"
  "\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb6\xb6\xb6"
  "\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6"
  "\xb6\xb6\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7"
  "\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8"
  "\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb9\xb9\xb9\xb9"
  "\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9"
  "\xb9\xba\xba\xba\xba\xba\xba\xba\xba\xba\xba\xba\xba\xba\xba\xba\xba\xba"
  "\xba\xba\xba\xba\xba\xba\xba\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb"
  "\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbc\xbc\xbc\xbc"
  "\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc"
  "\xbc\xbc\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd"
  "\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe"
  "\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbf\xbf"
  "\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf"
  "\xbf\xbf\xbf\xbf\xbf\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0"
  "\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc1\xc1\xc1\xc1\xc1\xc1"
  "\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1"
  "\xc1\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2"
  "\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3"
  "\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc4\xc4"
  "\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4"
  "\xc4\xc4\xc4\xc4\xc4\xc4\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5"
  "\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc6\xc6\xc6\xc6"
  "\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6"
  "\xc6\xc6\xc6\xc6\xc6\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7"
  "\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc8\xc8\xc8\xc8\xc8"
  "\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8"
  "\xc8\xc8\xc8\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9"
  "\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xca\xca\xca\xca\xca\xca"
  "\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca"
  "\xca\xca\xca\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb"
  "\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcc\xcc\xcc\xcc\xcc\xcc"
  "\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
  "\xcc\xcc\xcc\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
  "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xce\xce\xce\xce\xce"
  "\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce"
  "\xce\xce\xce\xce\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf"
  "\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xd0\xd0\xd0\xd0"
  "\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0"
  "\xd0\xd0\xd0\xd0\xd0\xd0\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1"
  "\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd2\xd2"
  "\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2"
  "\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3"
  "\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3"
  "\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4"
  "\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd5\xd5\xd5\xd5\xd5\xd5\xd5"
  "\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5"
  "\xd5\xd5\xd5\xd5\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6"
  "\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd7\xd7\xd7"
  "\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7"
  "\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8"
  "\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8"
  "\xd8\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9"
  "\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xda\xda\xda\xda\xda"
  "\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda"
  "\xda\xda\xda\xda\xda\xda\xda\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb"
  "\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb"
  "\xdb\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc"
  "\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdd\xdd\xdd\xdd\xdd"
  "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
  "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde"
  "\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde"
  "\xde\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf"
  "\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xe0\xe0\xe0\xe0"
  "\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0"
  "\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1"
  "\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1"
  "\xe1\xe1\xe1\xe1\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2"
  "\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe3"
  "\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3"
  "\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe4\xe4\xe4\xe4\xe4\xe4"
  "\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4"
  "\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5"
  "\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5"
  "\xe5\xe5\xe5\xe5\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6"
  "\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe7"
  "\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7"
  "\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe8\xe8\xe8\xe8\xe8"
  "\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8"
  "\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9"
  "\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9"
  "\xe9\xe9\xe9\xe9\xe9\xe9\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea"
  "\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea"
  "\xea\xea\xea\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb"
  "\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xec"
  "\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec"
  "\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xed\xed\xed"
  "\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed"
  "\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xee\xee\xee\xee\xee\xee"
  "\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee"
  "\xee\xee\xee\xee\xee\xee\xee\xee\xee\xef\xef\xef\xef\xef\xef\xef\xef\xef"
  "\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef"
  "\xef\xef\xef\xef\xef\xef\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0"
  "\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0"
  "\xf0\xf0\xf0\xf0\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1"
  "\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1"
  "\xf1\xf1\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2"
  "\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2"
  "\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3"
  "\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf4\xf4"
  "\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4"
  "\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf5\xf5\xf5"
  "\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5"
  "\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf6\xf6\xf6\xf6"
  "\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6"
  "\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf7\xf7\xf7\xf7\xf7\xf7"
  "\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7"
  "\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
  "\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
  "\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf9\xf9\xf9\xf9\xf9\xf9\xf9"
  "\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9"
  "\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa"
  "\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa"
  "\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb"
  "\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb"
  "\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc"
  "\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc"
  "\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd"
  "\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd"
  "\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe"
  "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe"
  "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff"
  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
;

// Like this:
//   tmp = (tmp<=0.00304) ? 12.92*tmp : 1.055*pow(tmp,1.0/2.4)-0.055;
//   tmp *= 255.9999;

static unsigned char linear_to_sRGB (float linear)
{
  unsigned slot;
  slot = linear*4096.0 + 0.5;
  if(slot>4095)
    {
      if(linear>0.5)
        slot = 4095;
      else
        slot = 0;
    }
  return linear_to_sRGB_table[slot];
}



////////////////////////////////////////////////////////////////////////////
// project 1st vector onto 2nd vector, then divide by 2nd magnitude
static double vdist(double x, double y, double z, double x2, double y2, double 
z2){
  double dot        = x *x2 + y *y2 + z *z2; // the dot product
  double magsquared = x2*x2 + y2*y2 + z2*z2; // the magnitude, squared
  return dot / magsquared;
}

///////////////////////////////////////////////////////////////////////////////
// passed original, foreground, and background images, return an alpha channel
static unsigned char *mkalpha(const unsigned char *o, const unsigned char *f, 
const unsigned char *b){
  unsigned char *ret = malloc(width*height);
  unsigned i = width*height;
  while(i--){
    unsigned char or = o[i*3+0];
    unsigned char og = o[i*3+1];
    unsigned char ob = o[i*3+2];
    unsigned char fr = f[i*3+0];
    unsigned char fg = f[i*3+1];
    unsigned char fb = f[i*3+2];
    unsigned char br = b[i*3+0];
    unsigned char bg = b[i*3+1];
    unsigned char bb = b[i*3+2];
    if(fr==br && fg==bg && fb==bb){
      // foreground and background are same color... uh oh
      ret[i] = 128; // a random value :-(
      continue;
    }
    if(or==br && og==bg && ob==bb){
      // original and background are same color: alpha is 0
      ret[i] = 0;
      continue;
    }
    if(fr==or && fg==og && fb==ob){
      // foreground and original are same color: alpha is 1.0 or 255
      ret[i] = 255;
      continue;
    }
    double tmp;
    // convert from sRGB to linear (gamma==1.0) RGB
    tmp = or / 255.0; double oR = (tmp<=0.03928) ? tmp/12.92 : 
pow((tmp+0.055)/1.055,2.4);
    tmp = og / 255.0; double oG = (tmp<=0.03928) ? tmp/12.92 : 
pow((tmp+0.055)/1.055,2.4);
    tmp = ob / 255.0; double oB = (tmp<=0.03928) ? tmp/12.92 : 
pow((tmp+0.055)/1.055,2.4);
    tmp = fr / 255.0; double fR = (tmp<=0.03928) ? tmp/12.92 : 
pow((tmp+0.055)/1.055,2.4);
    tmp = fg / 255.0; double fG = (tmp<=0.03928) ? tmp/12.92 : 
pow((tmp+0.055)/1.055,2.4);
    tmp = fb / 255.0; double fB = (tmp<=0.03928) ? tmp/12.92 : 
pow((tmp+0.055)/1.055,2.4);
    tmp = br / 255.0; double bR = (tmp<=0.03928) ? tmp/12.92 : 
pow((tmp+0.055)/1.055,2.4);
    tmp = bg / 255.0; double bG = (tmp<=0.03928) ? tmp/12.92 : 
pow((tmp+0.055)/1.055,2.4);
    tmp = bb / 255.0; double bB = (tmp<=0.03928) ? tmp/12.92 : 
pow((tmp+0.055)/1.055,2.4);
    int alpha = vdist(oR-bR, oG-bG, oB-bB,  fR-bR, fG-bG, fB-bB) * 255.9999;
    if(alpha<0)   alpha = 0;
    if(alpha>255) alpha = 255;
    ret[i] = alpha;
  }
  return ret;
}

static unsigned tinyval = 0;
static int user_r = 180;
static int user_g = 180;
static int user_b = 180;

///////////////////////////////////////////////////////////////////////////////
// Passed original, alpha, and background images, return a foreground image
// Fourth arg is optional foreground prototype, for filling alpha==0 areas
static unsigned char *mkforeg(const unsigned char *o, const unsigned char *a, 
const unsigned char *b, const unsigned char *f){
  unsigned char *ret = malloc(width*height*3);
  unsigned i = width*height;
  if(f){
    memcpy(ret,f,width*height*3);
  }else{
    memset(ret,180,width*height*3);
  }
  int fr = user_r;
  int fg = user_g;
  int fb = user_b;
  while(i--){
    unsigned char ax = a[i];
    if(ax==0){
      continue;
    }
    int or = o[i*3+0];
    int og = o[i*3+1];
    int ob = o[i*3+2];
    if(ax==255){
      ret[i*3+0] = or;
      ret[i*3+1] = og;
      ret[i*3+2] = ob;
      continue;
    }
    int br = b[i*3+0];
    int bg = b[i*3+1];
    int bb = b[i*3+2];

    double tmp;
    double alpha = ax / 255.0;

    if(abs(or-br)>tinyval){
      tmp = 
(sRGB_to_linear_table[or]-sRGB_to_linear_table[br]*(1.0-alpha))/alpha;
    }else{
      if(f) fr = f[i*3+0];
      tmp = sRGB_to_linear_table[or]*alpha + 
sRGB_to_linear_table[fr]*(1.0-alpha);
    }
    ret[i*3+0] = linear_to_sRGB(tmp);

    if(abs(og-bg)>tinyval){
      tmp = 
(sRGB_to_linear_table[og]-sRGB_to_linear_table[bg]*(1.0-alpha))/alpha;
    }else{
      if(f) fg = f[i*3+1];
      tmp = sRGB_to_linear_table[og]*alpha + 
sRGB_to_linear_table[fg]*(1.0-alpha);
    }
    ret[i*3+1] = linear_to_sRGB(tmp);


    if(abs(ob-bb)>tinyval){
      tmp = 
(sRGB_to_linear_table[ob]-sRGB_to_linear_table[bb]*(1.0-alpha))/alpha;
    }else{
      if(f) fb = f[i*3+2];
      tmp = sRGB_to_linear_table[ob]*alpha + 
sRGB_to_linear_table[fb]*(1.0-alpha);
    }
    ret[i*3+2] = linear_to_sRGB(tmp);

  }
  return ret;
}

///////////////////////////////////////////////////////////////////////////////
// Passed foreground, background, and alpha images, return a composited image
static unsigned char *composite(const unsigned char *f, const unsigned char *b, 
const unsigned char *a){
  unsigned char *ret = malloc(width*height*3);
  unsigned i = width*height;
  while(i--){
    unsigned char fr = f[i*3+0];
    unsigned char fg = f[i*3+1];
    unsigned char fb = f[i*3+2];
    unsigned char ax = a[i];
    unsigned char br = b[i*3+0];
    unsigned char bg = b[i*3+1];
    unsigned char bb = b[i*3+2];

    // shortcut some common cases
    if(ax==0){
      ret[i*3+0] = br;
      ret[i*3+1] = bg;
      ret[i*3+2] = bb;
      continue;
    }
    if(ax==255){
      ret[i*3+0] = fr;
      ret[i*3+1] = fg;
      ret[i*3+2] = fb;
      continue;
    }
    if(fr==br && fg==bg && fb==bb){
      // foreground and background are same color: alpha does not matter
      ret[i*3+0] = fr;
      ret[i*3+1] = fg;
      ret[i*3+2] = fb;
      continue;
    }

    double tmp;

    double alpha = ax / 255.0;

    tmp = sRGB_to_linear_table[fr]*alpha + sRGB_to_linear_table[br]*(1.0-alpha);
    ret[i*3+0] = linear_to_sRGB(tmp);

    tmp = sRGB_to_linear_table[fg]*alpha + sRGB_to_linear_table[bg]*(1.0-alpha);
    ret[i*3+1] = linear_to_sRGB(tmp);

    tmp = sRGB_to_linear_table[fb]*alpha + sRGB_to_linear_table[bb]*(1.0-alpha);
    ret[i*3+2] = linear_to_sRGB(tmp);
  }
  return ret;
}

///////////////////////////////////////////////////////////////////////////////
// Passed original and background images, return a foreground image
// (only suitable for pure light sources: fire w/o smoke, the glow of a bulb 
w/o the fixture...)
static unsigned char *purelight(const unsigned char *o, const unsigned char *b){
  unsigned char *ret = malloc(width*height*3);
  unsigned i = width*height;
  while(i--){
    unsigned char or = o[i*3+0];
    unsigned char og = o[i*3+1];
    unsigned char ob = o[i*3+2];
    unsigned char br = b[i*3+0];
    unsigned char bg = b[i*3+1];
    unsigned char bb = b[i*3+2];

    double r = sRGB_to_linear_table[or] - sRGB_to_linear_table[br];
    double g = sRGB_to_linear_table[og] - sRGB_to_linear_table[bg];
    double b = sRGB_to_linear_table[ob] - sRGB_to_linear_table[bb];

    double maxval = (r>g) ? r : g;
    maxval = (b>maxval) ? b : maxval;

    ret[i*3+0] = linear_to_sRGB(r/maxval);
    ret[i*3+1] = linear_to_sRGB(g/maxval);
    ret[i*3+2] = linear_to_sRGB(b/maxval);
  }
  return ret;
}

/////////////////////////////////////////////////////////////////////////////
int main(int argc, char *argv[]){
  unsigned char *foreground = NULL;
  unsigned char *background = NULL;
  unsigned char *original = NULL;
  unsigned char *alpha = NULL;

#ifdef DEBUG
  char *args[] = {
    "a.out",
    "-b",
    "bg.ppm",
    "-o",
    "original.ppm",
    "-f",
    "fgB.ppm",
    "-a",
    "aC.pgm",
    NULL
  };
  argc = 9;
  argv = args;
#endif

  if(argc<2) usage("not enough args",19,0);
  while(*(++argv)){
    if(argv[0][0]!='-' || !argv[0][1] || argv[0][2]) usage("bad arg",69,0);
    switch(argv[0][1]){
    default:
      usage("unknown arg",70,0);
    case 'f':
      if(foreground) usage("-f twice",71,0);
      foreground = readpnm(*(++argv),TYPE_PPM);
      if(!foreground) usage("-f failed",72,0);
      break;
    case 'T':
      if(tinyval != 0) usage("-T twice",91,0);
      tinyval = atoi(*(++argv));
      if(tinyval < 1 || tinyval > 254) usage("-T failed",92,0);
      break;
    case 'b':
      if(background) usage("-b twice",73,0);
      background = readpnm(*(++argv),TYPE_PPM);
      if(!background) usage("-b failed",74,0);
      break;
    case 'o':
      if(original) usage("-o twice",75,0);
      original = readpnm(*(++argv),TYPE_PPM);
      if(!original) usage("-o failed",76,0);
      break;
    case 'a':
      if(alpha) usage("-a twice",77,0);
      alpha = readpnm(*(++argv),TYPE_PGM);
      if(!alpha) usage("-a failed",78,0);
      break;
    }
  }

  if(original && alpha && background){
    unsigned char *ret = mkforeg(original,alpha,background,foreground);
    printf("P6\n%u %u\n255\n", width, height);
#ifndef DEBUG
    fwrite(ret, width*height*3, 1, stdout);
#endif
    return 0;
  }

  if(original && foreground && background && !alpha){
    unsigned char *ret = mkalpha(original,foreground,background);
    printf("P5\n%u %u\n255\n", width, height);
#ifndef DEBUG
    fwrite(ret, width*height, 1, stdout);
#endif
    return 0;
  }

  if(!original && foreground && background && alpha){
    unsigned char *ret = composite(foreground,background,alpha);
    printf("P6\n%u %u\n255\n", width, height);
#ifndef DEBUG
    fwrite(ret, width*height*3, 1, stdout);
#endif
    return 0;
  }

  if(original && !foreground && background && !alpha){
    unsigned char *ret = purelight(original,background);
    printf("P6\n%u %u\n255\n", width, height);
#ifndef DEBUG
    fwrite(ret, width*height*3, 1, stdout);
#endif
    return 0;
  }

  usage("odd arg combo",79,0);
  
  return 0; // keep gcc happy
}
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////


_______________________________________________
Tuxpaint-dev mailing list
[EMAIL PROTECTED]
http://tux4kids.net/mailman/listinfo/tuxpaint-dev

Reply via email to