/**
 * @file nebulae.c
 *
 * @brief Handle rendering and generating nebulae.
 */

#include <errno.h>
#include "nebulae.h"

#include "SDL_image.h"

#include "lephisto.h"
#include "log.h"
#include "opengl.h"
#include "lfile.h"
#include "rng.h"
#include "menu.h"
#include "player.h"
#include "pause.h"
#include "perlin.h"

#define NEBULAE_Z             16    /**< Z plane. */
#define NEBULAE_PUFFS         32    /**< Amount of puffs to generate. */
#define NEBULAE_PATH_BG       "gen/nebu_bg_%dx%d_%02d.png"  /**< Nebulae path format. */

#define NEBULAE_PUFF_BUFFER   300 /**< Nebulae buffer. */

/* Extern. */
extern double gui_xoff, gui_yoff;
extern Vec2 shake_pos;

/* The nebulae textures. */
static GLuint nebu_textures[NEBULAE_Z]; /**< BG Nebulae textures. */
static int nebu_w = 0;                  /**< BG Nebulae width. */
static int nebu_h = 0;                  /**< BG Nebulae height. */
static int nebu_pw;                     /**< BG Padded Nebulae width */
static int nebu_ph;                     /**< BG Padded Nebulae height. */

/* Information on rendering. */
static int cur_nebu[2] = { 0, 1 };      /**< Nebulae currently rendering. */
static unsigned int last_render = 0;    /**< When they where last rendered. */

/* Nebuale properties. */
static double nebu_view = 0.;           /**< How far player can see. */
static double nebu_dt   = 0.;           /**< How fast nebulae changes. */

/* Puff textures. */
static glTexture* nebu_pufftexs[NEBULAE_PUFFS]; /**< Nebulae puffs. */

/**
 * @struct NebulaePuff
 *
 * @brief Represents a nebulae puff.
 */
typedef struct NebulaePuff_ {
  double x;             /**< X Position. */
  double y;             /**< Y Position. */
  double height;        /**< Height vs player. */
  int tex;              /**< Texture. */
} NebulaePuff;
static NebulaePuff* nebu_puffs = NULL;    /**< Stack of puffs. */
static int nebu_npuffs = 0;               /**< Number of puffs. */

static int nebu_checkCompat(const char* file);
static void nebu_loadTexture(SDL_Surface* sur, int w, int h, GLuint tex);
static int nebu_generate(void);
static void nebu_generatePuffs(void);
static int saveNebulae(float* map, const uint32_t w, const uint32_t h,
    const char* file);
static SDL_Surface* loadNebulae(const char* file);
static SDL_Surface* nebu_surfaceFromNebulaeMap(float* map, const int w, const int h);

/**
 * @fn int nebu_init(void)
 *
 * @brief Initializes the nebulae.
 *    @return 0 on success.
 */
int nebu_init(void) {
  int i;
  char nebu_file[PATH_MAX];
  SDL_Surface* nebu_sur;
  int ret;

  /* Special code to regenerate the nebulae. */
  if((nebu_w == -9) && (nebu_h == -9))
    nebu_generate();

  /* Set expected sizes. */
  nebu_w = SCREEN_W;
  nebu_h = SCREEN_H;
  nebu_pw = gl_pot(nebu_w);
  nebu_ph = gl_pot(nebu_h);

  nebu_generatePuffs();

  /* Load each, checking for compatibility and padding. */
  glGenTextures(NEBULAE_Z, nebu_textures);
  for(i = 0; i < NEBULAE_Z; i++) {
    snprintf(nebu_file, PATH_MAX, NEBULAE_PATH_BG, nebu_w, nebu_h, i);

    if(nebu_checkCompat(nebu_file)) { /* Incompatable. */
      LOG("No nebulae found, generating (this may take a while).");

      /* So we generate and reload. */
      ret = nebu_generate();
      if(ret != 0) /* An error has happened - break recursivity. */
        return ret;

      return nebu_init();
    }

    /* Load the file. */
    nebu_sur = loadNebulae(nebu_file);
    if((nebu_sur->w != nebu_w) || (nebu_sur->h != nebu_h))
      WARN("Nebulae raw size doesn't match expected! (%dx%d instead of %dx%d)",
          nebu_sur->w, nebu_sur->h, nebu_pw, nebu_ph);

    /* Load the textures. */
    nebu_loadTexture(nebu_sur, nebu_pw, nebu_ph, nebu_textures[i]);
  }

  DEBUG("Loaded %d Nebulae Layers", NEBULAE_Z);
  return 0;
}

/**
 * @fn static void nebu_loadTexture(SDL_Surface* sur, int w, int h, GLuint tex)
 *
 * @brief Load sur into tex, check for expected size of w and h.
 *    @param sur Surface to load into texture.
 *    @param w Expected width of surface.
 *    @param h Expected height of surface.
 *    @param tex Already generated texture to load into.
 */
static void nebu_loadTexture(SDL_Surface* sur, int w, int h, GLuint tex) {
  SDL_Surface* nebu_sur;

  nebu_sur = gl_prepareSurface(sur);
  if((w != 0) && (h != 0) &&
      ((nebu_sur->w != w) || (nebu_sur->h != h))) {
    
    WARN("Nebulae size doesn't match expected! (%dx%d instead of %dx%d)",
        nebu_sur->w, nebu_sur->h, nebu_pw, nebu_ph);
    return;
  }

  /* Load the textures. */
  glBindTexture(GL_TEXTURE_2D, tex);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

  /* Store into opengl saving only alpha channel in video memory. */
  SDL_LockSurface(nebu_sur);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, nebu_sur->w, nebu_sur->h,
      0, GL_RGBA, GL_UNSIGNED_BYTE, nebu_sur->pixels);
  SDL_UnlockSurface(nebu_sur);

  SDL_FreeSurface(nebu_sur);
  gl_checkErr();
}

/**
 * @fn void nebu_exit(void)
 *
 * @brief Clean up the nebu subsystem.
 */
void nebu_exit(void) {
  int i;

  glDeleteTextures(NEBULAE_Z, nebu_textures);

  for(i = 0; i < NEBULAE_PUFFS; i++)
    gl_freeTexture(nebu_pufftexs[i]);
}

/**
 * @fn void nebu_render(const double dt)
 *
 * @brief Render the nebulae.
 *    @param dt Current delta tick.
 */
void nebu_render(const double dt) {
  unsigned int t;
  double ndt;
  double tw, th;
  GLfloat col[4];
  int tmp;

  /* Calculate frame to draw. */
  t = SDL_GetTicks();
  ndt = (t - last_render) / 1000.;
  if(ndt > nebu_dt) { /* Time to change. */
    tmp = cur_nebu[0];
    cur_nebu[0] += cur_nebu[0] - cur_nebu[1];
    cur_nebu[1] = tmp;
    if(cur_nebu[0]+1 > NEBULAE_Z)
      cur_nebu[0] = NEBULAE_Z - 2;
    else if(cur_nebu[0] < 0)
      cur_nebu[0] = 1;

    last_render = t;
    ndt = 0.;
  }

  /* Set colour. */
  col[0] = cPurple.r;
  col[1] = cPurple.g;
  col[2] = cPurple.b;
  col[3] = ndt / nebu_dt;

  tw = (double)nebu_w / (double)nebu_pw;
  th = (double)nebu_h / (double)nebu_ph;

  /* Set up the targets. */
  /* Texture 0. */
  glActiveTexture(GL_TEXTURE0);
  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, nebu_textures[cur_nebu[1]]);

  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

  /* Texture 1. */
  glActiveTexture(GL_TEXTURE1);
  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, nebu_textures[cur_nebu[0]]);

  /* Prepare it. */
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
  glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
  glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_INTERPOLATE);
  /* Colour. */
  glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, col);

  /* Arguments. */
  /* Arg0 */
  glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_CONSTANT);
  glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);
  glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
  glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
  /* Arg 1. */
  glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
  glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PREVIOUS);
  glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
  glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
  /* Arg 2. */
  glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT);
  glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_ALPHA, GL_CONSTANT);
  glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA);
  glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA);

  /* Compensate possible rubmle. */
  if(!paused) {
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glTranslated(shake_pos.x, shake_pos.y, 0.);
  }

  /* Now render. */
  glBegin(GL_QUADS);
    glMultiTexCoord2d(GL_TEXTURE0, 0., 0.);
    glMultiTexCoord2d(GL_TEXTURE1, 0., 0.);
    glVertex2d(-SCREEN_W/2., -SCREEN_H/2.);

    glMultiTexCoord2d(GL_TEXTURE0, tw, 0.);
    glMultiTexCoord2d(GL_TEXTURE1, tw, 0.);
    glVertex2d(SCREEN_W/2., -SCREEN_H/2.);
    
    glMultiTexCoord2d(GL_TEXTURE0, tw, th);
    glMultiTexCoord2d(GL_TEXTURE1, tw, th);
    glVertex2d(SCREEN_W/2., SCREEN_H/2.);
  
    glMultiTexCoord2d(GL_TEXTURE0, 0., th);
    glMultiTexCoord2d(GL_TEXTURE1, 0., th);
    glVertex2d(-SCREEN_W/2., SCREEN_H/2.);
  glEnd();

  if(!paused)
    glPopMatrix(); /* GL_PROJECTION */

  /* Set values to defaults. */
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  glDisable(GL_TEXTURE_2D);
  glActiveTexture(GL_TEXTURE0);
  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  glDisable(GL_TEXTURE_2D);

  /* Did anything fail? */
  gl_checkErr();

  /* Now render the puffs. */
  nebu_renderPuffs(dt, 1);
}

/**
 * @fn void nebu_renderOverlay(const double dt)
 *
 * @brief Renders the nebulae overlay (hides what player can't see).
 *    @param dt Current delta tick.
 */
void nebu_renderOverlay(const double dt) {
#define ANG45   0.70710678118654757
#define COS225  0.92387953251128674
#define SIN225  0.38268343236508978

  /* Render the puffs. */
  nebu_renderPuffs(dt, 0);

  /* Prepare the matrix. */
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glTranslated(gui_xoff, gui_yoff, 0.);
  if(!paused)
    glTranslated(gui_xoff+shake_pos.x, gui_yoff+shake_pos.y, 0.);

  /* Mask for area player can still see (partially). */
  glShadeModel(GL_SMOOTH);
  glBegin(GL_TRIANGLE_FAN);
    ACOLOUR(cPurple,                0.);
    glVertex2d(0.,                  0.);
    ACOLOUR(cPurple,                1.);
    glVertex2d(-nebu_view,          0.);
    glVertex2d(-nebu_view*COS225,   nebu_view*SIN225);
    glVertex2d(-nebu_view*ANG45,    nebu_view*ANG45);
    glVertex2d(-nebu_view*SIN225,   nebu_view*COS225);
    glVertex2d( 0.,                 nebu_view);
    glVertex2d( nebu_view*SIN225,   nebu_view*COS225);
    glVertex2d( nebu_view*ANG45,    nebu_view*ANG45);
    glVertex2d( nebu_view*COS225,   nebu_view*SIN225);
    glVertex2d( nebu_view,          0.);
    glVertex2d( nebu_view*COS225,   -nebu_view*SIN225);
    glVertex2d( nebu_view*ANG45,    -nebu_view*ANG45);
    glVertex2d( nebu_view*SIN225,   -nebu_view*COS225);
    glVertex2d( 0.,                 -nebu_view);
    glVertex2d(-nebu_view*SIN225,   -nebu_view*COS225);
    glVertex2d(-nebu_view*ANG45,    -nebu_view*ANG45);
    glVertex2d(-nebu_view*COS225,   -nebu_view*SIN225);
    glVertex2d(-nebu_view,          0.);
  glEnd(); /* GL_TRIANGLE_FAN */

  /* Solid nebulae for areas the player can't see. */
  glShadeModel(GL_FLAT);
  ACOLOUR(cPurple, 1.);

  glBegin(GL_TRIANGLE_FAN);
    /* Top left. */
    glVertex2d(-SCREEN_W/2.-gui_xoff,     SCREEN_H/2.-gui_yoff);
    glVertex2d(-nebu_view,                0.);
    glVertex2d(-nebu_view*COS225,         nebu_view*SIN225);
    glVertex2d(-nebu_view*ANG45,          nebu_view*ANG45);
    glVertex2d(-nebu_view*SIN225,         nebu_view*COS225);
    glVertex2d(0.,                        nebu_view);
    glVertex2d( SCREEN_W/2.-gui_xoff,     SCREEN_H/2.-gui_yoff);
  glEnd(); /* GL_TRIANGLE_FAN */
  glBegin(GL_TRIANGLE_FAN);
    /* Top right. */
    glVertex2d( SCREEN_W/2.-gui_xoff,     SCREEN_H/2.-gui_yoff);
    glVertex2d( 0.,                       nebu_view);
    glVertex2d( nebu_view*SIN225,         nebu_view*COS225);
    glVertex2d( nebu_view*ANG45,          nebu_view*ANG45);
    glVertex2d( nebu_view*COS225,         nebu_view*SIN225);
    glVertex2d( nebu_view,                0.);
    glVertex2d( SCREEN_W/2.-gui_xoff,     -SCREEN_H/2.-gui_yoff);
  glEnd(); /* GL_TRIANGLE_FAN */
  glBegin(GL_TRIANGLE_FAN);
    /* Bottom right. */
    glVertex2d( SCREEN_W/2.-gui_xoff,     -SCREEN_H/2.-gui_yoff);
    glVertex2d( nebu_view,                0.);
    glVertex2d( nebu_view*COS225,         -nebu_view*SIN225);
    glVertex2d( nebu_view*ANG45,          -nebu_view*ANG45);
    glVertex2d( nebu_view*SIN225,         -nebu_view*COS225);
    glVertex2d( 0.,                       -nebu_view);
    glVertex2d( -SCREEN_W/2.-gui_xoff,    -SCREEN_H/2.-gui_yoff);
  glEnd(); /* GL_TRIANGLE_FAN */
  glBegin(GL_TRIANGLE_FAN);
    /* Bottom left */
    glVertex2d( -SCREEN_W/2.-gui_xoff,    -SCREEN_H/2.-gui_yoff);
    glVertex2d( 0.,                       -nebu_view);
    glVertex2d(-nebu_view*SIN225,         -nebu_view*COS225);
    glVertex2d(-nebu_view*ANG45,          -nebu_view*ANG45);
    glVertex2d(-nebu_view*COS225,         -nebu_view*SIN225);
    glVertex2d(-nebu_view,                0.);
    glVertex2d(-SCREEN_W/2.-gui_xoff,     SCREEN_H/2.-gui_yoff);
  glEnd(); /* GL_TRIANGLE_FAN */

  glPopMatrix();

  gl_checkErr();

#undef ANG45
#undef COS225
#undef SIN225
}

/**
 * @fn void nebu_renderPuffs(const double dt, int below_player)
 *
 * @brief Render the puffs.
 *    @param dt Current delta tick.
 *    @param below_player Render the puffs below player or above?
 */
void nebu_renderPuffs(const double dt, int below_player) {
  int i;

  /* Main menu shouldn't have puffs. */
  if(menu_isOpen(MENU_MAIN)) return;

  /* Seperate by layers. */
  for(i = 0; i < nebu_npuffs; i++) {
    if((below_player && (nebu_puffs[i].height < 1.)) ||
        (!below_player && (nebu_puffs[i].height > 1.))) {
      
      /* Calculate new position. */
      if(!paused && (player != NULL)) {
        nebu_puffs[i].x -= player->solid->vel.x * nebu_puffs[i].height * dt;
        nebu_puffs[i].y -= player->solid->vel.y * nebu_puffs[i].height * dt;
      }

      /* Check boundaries. */
      if(nebu_puffs[i].x > SCREEN_W + NEBULAE_PUFF_BUFFER)
        nebu_puffs[i].x = -NEBULAE_PUFF_BUFFER;
      else if(nebu_puffs[i].y > SCREEN_H + NEBULAE_PUFF_BUFFER)
        nebu_puffs[i].y = -NEBULAE_PUFF_BUFFER;
      else if(nebu_puffs[i].x < -NEBULAE_PUFF_BUFFER)
        nebu_puffs[i].x = SCREEN_W + NEBULAE_PUFF_BUFFER;
      else if(nebu_puffs[i].y < -NEBULAE_PUFF_BUFFER)
        nebu_puffs[i].y = SCREEN_H + NEBULAE_PUFF_BUFFER;

      /* Render. */
      gl_blitStatic(nebu_pufftexs[nebu_puffs[i].tex],
          nebu_puffs[i].x, nebu_puffs[i].y, &cPurple);
    }
  }
}

/**
 * @fn void nebu_prep(double density, double volatility)
 *
 * @brief Prepares the nebulae to be rendered.
 *    @param density Density of the nebulae (0-1000).
 *    @param volatility Volatility of the nebulae (0-1000).
 */
void nebu_prep(double density, double volatility) {
  (void)volatility;
  int i;

  nebu_view = 1000. - density;        /* At density 1000 you're blind. */
  nebu_dt = 2000. / (density + 100.); /* Faster at higher density. */

  nebu_npuffs = density/4.;
  nebu_puffs = realloc(nebu_puffs, sizeof(NebulaePuff)*nebu_npuffs);
  for(i = 0; i < nebu_npuffs; i++) {
    /* Position. */
    nebu_puffs[i].x = (double)RNG(-NEBULAE_PUFF_BUFFER,
        SCREEN_W + NEBULAE_PUFF_BUFFER);
    nebu_puffs[i].y = (double)RNG(-NEBULAE_PUFF_BUFFER,
        SCREEN_H + NEBULAE_PUFF_BUFFER);

    /* Maybe make the size related. */
    nebu_puffs[i].tex = RNG(0, NEBULAE_PUFFS-1);
    nebu_puffs[i].height = RNGF() + 0.2;
  }
}

/**
 * @fn void nebu_forceGenerate(void)
 *
 * @brief Force generation of new nebulae on init.
 */
void nebu_forceGenerate(void) {
  nebu_w = nebu_h = -9; /* Magic numbers. ^.^ */
}

/**
 * @fn static int nebu_generate(void)
 * @brief Generates the nebulae.
 *    @return 0 on success.
 */
static int nebu_generate(void) {
  int i;
  float* nebu;
  char nebu_file[PATH_MAX];
  int w, h;
  int ret;

  w = SCREEN_W;
  h = SCREEN_H;

  /* Generate all the nebulae backgrounds. */
  nebu = noise_genNebulaeMap(w, h, NEBULAE_Z, 5.);
  lfile_dirMakeExist("gen");

  /* Save each nebulae as an image. */
  for(i = 0; i < NEBULAE_Z; i++) {
    snprintf(nebu_file, PATH_MAX, NEBULAE_PATH_BG, w, h, i);
    ret = saveNebulae(&nebu[i*w*h], w, h, nebu_file);
    if(ret != 0) break; /* An error has happened. */
  }

  /* Cleanup. */
  free(nebu);
  return ret;
}

/**
 * @fn static void nebu_generatePuffs(void)
 *
 * @brief Generate nebulae puffs.
 */
static void nebu_generatePuffs(void) {
  int i;
  int w, h;
  SDL_Surface* sur;
  float* nebu;

  /* Generate the nebulae puffs. */
  for(i = 0; i < NEBULAE_PUFFS; i++) {
    /* Generate the nebulae. */
    w = h = RNG(20, 64);
    nebu = noise_genNebulaePuffMap(w, h, 1.);
    sur = nebu_surfaceFromNebulaeMap(nebu, w, h);
    free(nebu);

    /* Load the textures. */
    nebu_pufftexs[i] = gl_loadImage(sur);
  }
}

/**
 * @fn static int nebu_checkCompat(const char* file)
 *
 * @brief Check the validity of a nebulae.
 *    @param file Path of the nebulae to check (relative to base directory).
 *    @return 0 on success.
 */
static int nebu_checkCompat(const char* file) {
  if(lfile_fileExists(file) == 0) /* First check to see if file exists. */
    return -1;
  return 0;
}

/**
 * @fn static int saveNebulae(float* map, const uint32_t w, const uint32_t h,
 *                  const char* file)
 * 
 * @brief Save a nebulae.
 *    @param map Nebulae map to save.
 *    @param w Width of nebulae map.
 *    @param h Height of nebulae map.
 *    @param file Path to save into.
 *    @return 0 on success.
 */
static int saveNebulae(float* map, const uint32_t w, const uint32_t h,
    const char* file) {
  
  char file_path[PATH_MAX];
  SDL_Surface* sur;
  int ret;

  /* Fix surface. */
  sur = nebu_surfaceFromNebulaeMap(map, w, h);
  
  /* Save. */
  ret = 0;
  snprintf(file_path, PATH_MAX, "%s%s", lfile_basePath(), file);
  ret = SDL_savePNG(sur, file_path);

  /* Cleanup. */
  SDL_FreeSurface(sur);

  return ret;
}

/**
 * @fn static SDL_Surface* loadNebulae(const char* file)
 *
 * @brief Loads the nebulae from file.
 *    @param file Path of the nebulae to load. Relative to base directory.
 *    @return An SDL surface with the nebulae.
 */
static SDL_Surface* loadNebulae(const char* file) {
  char file_path[PATH_MAX];
  SDL_Surface* sur;

  /* Load the file. */
  snprintf(file_path, PATH_MAX, "%s%s", lfile_basePath(), file);

  sur = IMG_Load(file_path);
  if(sur == NULL) {
    ERR("Unable to load Nebulae image: %s", file);
    return NULL;
  }

  return sur;
}

/**
 * @fn SDL_Surface* nebu_surfaceFromNebulaeMap(float* map, const int w, const int h)
 *
 * @brief Generate an SDL_Surface from a 2d nebulae map.
 *    @param map Nebulae map to use.
 *    @param w Map width.
 *    @param h Map height.
 *    @return An SDL Surface with the nebulae.
 */
SDL_Surface* nebu_surfaceFromNebulaeMap(float* map, const int w, const int h) {
  int i;
  SDL_Surface* sur;
  uint32_t* pix;
  double c;

  /* The good surface. */
  sur = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, RGBAMASK);
  pix = sur->pixels;

  /* Convert from mapping to actual colours. */
  SDL_LockSurface(sur);
  for(i = 0; i < h*w; i++) {
    c = map[i];
    pix[i] = RMASK + BMASK + GMASK + (AMASK & (uint32_t)((double)AMASK*c));
  }
  SDL_UnlockSurface(sur);

  return sur;
}

