/*************************************/
/* GIMP image manipulation routines. */
/*************************************/

#include "lighting_image.h"

GDrawable *input_drawable,*output_drawable;
GPixelRgn source_region, dest_region;

GDrawable *bump_drawable = NULL;
GPixelRgn bump_region;

GDrawable *env_drawable = NULL;
GPixelRgn env_region;

guchar *preview_rgb_data = NULL;
GdkImage *image = NULL;

glong maxcounter;
gint imgtype,width,height,env_width,env_height,in_channels,out_channels;
GckRGB background;

gint border_x1,border_y1,border_x2,border_y2;

guchar sinemap[256],spheremap[256],logmap[256];

/******************/
/* Implementation */
/******************/

guchar peek_map(GPixelRgn *region,gint x,gint y)
{
  guchar data;

  gimp_pixel_rgn_get_pixel(region,&data,x,y);
  
  return(data);
}

GckRGB peek(gint x,gint y)
{
  guchar data[4];
  GckRGB color;

  gimp_pixel_rgn_get_pixel(&source_region,data,x,y);

  color.r=(gdouble)(data[0])/255.0;
  color.g=(gdouble)(data[1])/255.0;
  color.b=(gdouble)(data[2])/255.0;

  if (input_drawable->bpp==4)
    {
      if (in_channels==4)
        color.a=(gdouble)(data[3])/255.0;
      else
        color.a=1.0;
    }
  else
    color.a=1.0;

  return(color);
}

GckRGB peek_env_map(gint x,gint y)
{
  guchar data[4];
  GckRGB color;

 if (x<0)
    x=0;
  else if (x>=env_width)
    x=env_width-1;
  if (y<0)
    y=0;
  else if (y>=env_height)
    y=env_height-1;

  gimp_pixel_rgn_get_pixel(&env_region,data,x,y);

  color.r=(gdouble)(data[0])/255.0;
  color.g=(gdouble)(data[1])/255.0;
  color.b=(gdouble)(data[2])/255.0;
  color.a=1.0;

  return(color);
}

void poke(gint x,gint y,GckRGB *color)
{
  static guchar data[4];

  if (x<0)
    x=0;
  else if (x>=width)
    x=width-1;
  if (y<0)
    y=0;
  else if (y>=height)
    y=height-1;

  data[0]=(guchar)(color->r*255.0);
  data[1]=(guchar)(color->g*255.0);
  data[2]=(guchar)(color->b*255.0);
  data[3]=(guchar)(color->a*255.0);
  
  gimp_pixel_rgn_set_pixel(&dest_region,data,x,y);
}

gint check_bounds(gint x,gint y)
{
  if (x<border_x1 || y<border_y1 || x>=border_x2 || y>=border_y2)
    return(FALSE);
  else
    return(TRUE);
}

GckVector3 int_to_pos(gint x,gint y)
{
  GckVector3 pos;

  if (width>=height)
    {
      pos.x=(gdouble)x/(gdouble)width;
      pos.y=(gdouble)y/(gdouble)width;

      pos.y+=0.5*(1.0-(gdouble)height/(gdouble)width); 
    }
  else
    {
      pos.x=(gdouble)x/(gdouble)height;
      pos.y=(gdouble)y/(gdouble)height;

      pos.x+=0.5*(1.0-(gdouble)width/(gdouble)height);
    }
  
  pos.z=0.0;
  return(pos);
}

GckVector3 int_to_posf(gdouble x,gdouble y)
{
  GckVector3 pos;

  if (width>=height)
    {
      pos.x=x/(gdouble)width;
      pos.y=y/(gdouble)width;

      pos.y+=0.5*(1.0-(gdouble)height/(gdouble)width); 
    }
  else
    {
      pos.x=x/(gdouble)height;
      pos.y=y/(gdouble)height;

      pos.x+=0.5*(1.0-(gdouble)width/(gdouble)height);
    }
  
  pos.z=0.0;
  return(pos);
}

void pos_to_int(gdouble x,gdouble y,gint *scr_x,gint *scr_y)
{
  if (width>=height)
    {
      y-=0.5*(1.0-(gdouble)height/(gdouble)width);
      *scr_x=(gint)((x*(gdouble)width)+0.5);
      *scr_y=(gint)((y*(gdouble)width)+0.5);
    }
  else
    {
      x-=0.5*(1.0-(gdouble)width/(gdouble)height);

      *scr_x=(gint)((x*(gdouble)height)+0.5);
      *scr_y=(gint)((y*(gdouble)height)+0.5);
    }
}

void pos_to_float(gdouble x,gdouble y,gdouble *xf,gdouble *yf)
{
  if (width>=height)
    {
      y-=0.5*(1.0-(gdouble)height/(gdouble)width);
      *xf=x*(gdouble)width;
      *yf=y*(gdouble)width;
    }
  else
    {
      x-=0.5*(1.0-(gdouble)width/(gdouble)height);

      *xf=x*(gdouble)height;
      *yf=y*(gdouble)height;
    }
}

/**********************************************/
/* Compute the image color at pos (u,v) using */
/* Quartics bilinear interpolation stuff.     */
/**********************************************/

GckRGB get_image_color(gdouble u,gdouble v,gint *inside)
{
  gint      x1, y1, x2, y2;
  GckRGB p[4];
 
  x1=(gint)(u+0.5);
  y1=(gint)(v+0.5);

  if (check_bounds(x1,y1)==FALSE)
    {
      *inside=FALSE;
      return(background);
    }

  x2 = (x1 + 1);
  y2 = (y1 + 1);

  if (check_bounds(x2,y2)==FALSE)
   {
     *inside=TRUE;
     return(peek(x1,y1));
   }

  *inside=TRUE;
  p[0] = peek(x1, y1);
  p[1] = peek(x2, y1);
  p[2] = peek(x1, y2);
  p[3] = peek(x2, y2);

  return(gck_bilinear_rgba(u, v, p));
}

gdouble get_map_value(GPixelRgn *region, gdouble u,gdouble v, gint *inside)
{
  gint    x1, y1, x2, y2;
  gdouble p[4];
 
  x1=(gint)(u+0.5);
  y1=(gint)(v+0.5);
  
  x2 = (x1 + 1);
  y2 = (y1 + 1);

  if (check_bounds(x2,y2)==FALSE)
   {
     *inside=TRUE;
     return((gdouble)peek_map(region, x1,y1));
   }

  *inside=TRUE;
  p[0] = (gdouble)peek_map(region, x1, y1);
  p[1] = (gdouble)peek_map(region, x2, y1);
  p[2] = (gdouble)peek_map(region, x1, y2);
  p[3] = (gdouble)peek_map(region, x2, y2);

  return(gck_bilinear(u, v, p));
}

void compute_maps(void)
{
  gint x;
  gdouble val,c,d;

  /* Compute Sine, Log ans Spherical transfer function maps */
  /* ====================================================== */

  c=1.0/255.0;
  d=1.15*255.0;
  
  for (x=0;x<256;x++)
    {
      sinemap[x]=(guchar)(255.0*(0.5*(sin((M_PI*c*(gdouble)x)-0.5*M_PI)+1.0)));
      spheremap[x]=(guchar)(255.0*(sqrt(sin(M_PI*(gdouble)x/512.0))));
      val=(d*exp(-1.0/(8.0*c*((gdouble)x+5.0))));
      
      if (val>255.0)
        val=255.0;
      logmap[x]=(guchar)val;
    }

}

/****************************************/
/* Allocate memory for temporary images */
/****************************************/

gint image_setup(GDrawable *drawable,gint interactive)
{
  glong numbytes;

  compute_maps();
  
  /* Get some useful info on the input drawable */
  /* ========================================== */

  input_drawable=drawable;
  output_drawable=drawable;

  gimp_drawable_mask_bounds (drawable->id, &border_x1, &border_y1, &border_x2, &border_y2);

  width=input_drawable->width;
  height=input_drawable->height;

  gimp_pixel_rgn_init (&source_region, input_drawable, 0, 0, width, height, FALSE, FALSE);

  maxcounter=(glong)width*(glong)height;

  /* Assume at least RGB */
  /* =================== */

  in_channels=3;
  if (gimp_drawable_has_alpha(input_drawable->id)==TRUE)
    in_channels++;

  if (interactive==TRUE)
    {
      /* Allocate memory for temp. images */
      /* ================================ */

      image=gdk_image_new(GDK_IMAGE_FASTEST,appwin->visinfo->visual,PREVIEW_WIDTH,PREVIEW_HEIGHT);

      if (image==NULL)
        return(FALSE);

      numbytes=(glong)PREVIEW_WIDTH*(glong)PREVIEW_HEIGHT*3;
      preview_rgb_data=(guchar *)malloc((size_t)numbytes);

      if (preview_rgb_data==NULL)
        return(FALSE);

      memset(preview_rgb_data,0,numbytes);
    
      /* Convert from raw RGB to GdkImage */
      /* ================================ */

      gck_rgb_to_gdkimage(appwin->visinfo,preview_rgb_data,image,PREVIEW_WIDTH,PREVIEW_HEIGHT);
    }

  return(TRUE);
}

