/* $Id: readtexture.c,v 1.12 2003/03/21 01:10:49 d3august Exp $
*/
/*  xtraceroute - graphically show traceroute information.
 *  Copyright (C) 1996-2003  Björn Augustsson 
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 *  02111-1307, USA.
 */

#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "xt.h"

/**
 * Readtexture does just what the name suggests.
 */

GdkPixbuf *
readTexture(const char *imgname)
{
  GdkPixbuf *img;

  DPRINTF("Loading texture from file \"%s\"... ", imgname);

  img = gdk_pixbuf_new_from_file(imgname);
  if (img == NULL) 
    {
      fprintf(stderr, "Couldn't load texture file %s\n", imgname);
      exit(EXIT_FAILURE);
    }

  DPRINTF("Done!\n\tTexture size is %dx%d\n",
	  gdk_pixbuf_get_width(img), gdk_pixbuf_get_height(img));

  return img;
}


extern void 
get_sun_position(double* , double*);


#define DAY 0
#define NIGHT 0xFF
#define SET_ALPHA(x, y, val) *(pt+4*(width*y+x)+3) = val
#define SET_WHOLE_ROWS(from, to, val) \
 {                                    \
   int y;                             \
   for(y=from ; y<to ; y++)           \
     {                                \
       int x;                         \
       for(x=0 ; x<width ; x++)       \
          SET_ALPHA(x, y, val);       \
     }                                \
  }


/**
 * A high alpha value in the night texture means it's dark
 * Most of this code taken from xplanet. See astronomy.c.
 */
static void
update_alpha_in_texture(GdkPixbuf* tex)
{
  int y;
  int width  = gdk_pixbuf_get_width(tex);
  int height = gdk_pixbuf_get_height(tex);
  guchar* pt = gdk_pixbuf_get_pixels(tex);
  int polar_rows, inoon;
  
  float lat_per_row = 180.0/(float)height;
  double sunlat, sunlon;
  
  get_sun_position(&sunlat, &sunlon);

  DPRINTF("SUN:   lat: %2.2f     lon: %2.2f\n", sunlat, sunlon);

  /* number of rows at top and bottom that are in polar day/night */
  polar_rows = abs((int) (sunlat/lat_per_row));
  
  if (sunlat < 0) // North pole is dark
    {
      SET_WHOLE_ROWS(0,                 polar_rows, NIGHT);
      SET_WHOLE_ROWS(height-polar_rows, height,     DAY);
      }
  else            // South pole is dark
    {
      SET_WHOLE_ROWS(0,                 polar_rows,   DAY);
      SET_WHOLE_ROWS(height-polar_rows, height,       NIGHT);
    }
  
  // subsolar longitude pixel column - this is where it's noon
  inoon = (int) (width/2 * (sunlon / M_PI - 1));
  while (inoon < 0) inoon += width;
  while (inoon >= width) inoon -= width;
  
  for (y=polar_rows ; y<height-polar_rows ; y++)
    {
      double length_of_day, H0;
      int ilight, dark_pixels;
      
      /* compute length of daylight as a fraction of the day at 
       * the current latitude.  Based on Chapter 42 of Astronomical 
       * Formulae for Calculators by Meeus. */
      
      H0 = tan(M_PI_2 - M_PI*(float)y/(float)height) * tan(sunlat*torad);
      if (H0 > 1) 
	length_of_day = 1;
      else if (H0 < -1) 
	length_of_day = 0;
      else 
	length_of_day = 1.0 - (acos(H0) / M_PI);
      
      /* ilight = number of pixels from noon to the terminator. */
      ilight = (int) (width/2 * length_of_day + 0.5);
      
      /* dark_pixels = number of pixels that are in darkness at the current 
	 latitude */
      dark_pixels = width - 2 * ilight;
      
      // start at the evening terminator
      {
	int pos = inoon + ilight;
	int j;
	
	for (j=0 ; j<dark_pixels ; j++)
	  {
	    if (pos >= width) 
	      pos -= width;
	    SET_ALPHA(pos, y, NIGHT);
	    pos++;
	  }
	for (j=dark_pixels ; j<width ; j++)
	  {
	    if (pos >= width) 
	      pos -= width;
	    SET_ALPHA(pos, y, DAY);
	    pos++;
	  }
      }
    }
}


/**
 * Makes a composite texture from the day and night images, according to 
 * what time it is.
 *
 * dest needs to be at least as big as the biggest one of the night and
 * day textures.
 *
 * Note: Allocates memory. (For the dest argument.)
 */

void
composite_texture(GdkPixbuf* night, const GdkPixbuf* day, GdkPixbuf** dest)
{
  int dest_w = MAX(gdk_pixbuf_get_width(night),  gdk_pixbuf_get_width(day)); 
  int dest_h = MAX(gdk_pixbuf_get_height(night), gdk_pixbuf_get_height(day)); 

  
  if(!gdk_pixbuf_get_has_alpha(night))
    night = gdk_pixbuf_add_alpha(night, FALSE, 0,0,0);
  
  update_alpha_in_texture(night);

  
  {

#if 0
  // Testpattern.
    pt += 3;

    for(i=0; i < height ; i++)
      {
	for(j=0; j < width ; j++)
	  {
	    *(pt) = j%255;
	    
	    pt += 4;
	  }
      }
#endif //0 
  }

  /* Copy day texture to dest */
  
  *dest = gdk_pixbuf_scale_simple(day, dest_w, dest_h, GDK_INTERP_HYPER);
  
  /* Composite night onto dest */
  {
    double scale_x = (double)gdk_pixbuf_get_width(*dest)  / (double)gdk_pixbuf_get_width(night);
    double scale_y = (double)gdk_pixbuf_get_height(*dest) / (double)gdk_pixbuf_get_height(night);

    gdk_pixbuf_composite(night, *dest,
			 0,0,            // dest_x, dest_y,
			 dest_w, dest_h,
			 0.0, 0.0,       // offset_x, offset_y,
			 scale_x, scale_y, 
			 GDK_INTERP_HYPER,
			 255);    
  }

  return;
}


syntax highlighted by Code2HTML, v. 0.9.1