/*
  Cockos WDL - LICE - Lightweight Image Compositing Engine
  Copyright (C) 2007 and later, Cockos Incorporated
  File: lice_texgen.cpp (LICE texture generator routines)
  See lice.h for license and other information
*/


#ifndef WDL_NO_DEFINE_MINMAX
#define WDL_NO_DEFINE_MINMAX
#endif
#include "lice.h"
#include <math.h>

void LICE_TexGen_Marble(LICE_IBitmap *dest, const RECT *rect, float rv, float gv, float bv, float intensity)
{
  int span=dest->getRowSpan();
  int w = dest->getWidth();
  int h = dest->getHeight();
  int x = 0;
  int y = 0;
  if(rect)
  {
    x = rect->left;
    y = rect->top;
    w = rect->right - rect->left;
    h = rect->bottom - rect->top;
  }

  if (x<0) { w+=x; x=0; }
  if (y<0) { h+=y; y=0; }

  const int destbm_w = dest->getWidth(), destbm_h = dest->getHeight();
  if (w<1 || h < 1 || x >= destbm_w || y >= destbm_h) return;

  if (w>destbm_w-x) w=destbm_w-x;
  if (h>destbm_h-y) h=destbm_h-y;


  LICE_pixel *startp = dest->getBits();
  if (dest->isFlipped())
  {
    startp += x + (dest->getHeight()-1-y)*span;
    span=-span;
  }
  else startp  += x + y*span;

  //simple 16bit marble noise generator

#define ROL(x,y) ((x<<(y))|(((unsigned short)x)>>(16-(y))))
#define ROR(x,y) ((((unsigned short)x)>>(y))|(x<<(16-(y))))

  intensity/=1024.0f;
  int maxc = 0;
  {
    LICE_pixel *p = startp;
    short n1 = 0, n2 = 0;
    for(int i=0;i<h;i++)
    {
      for(int j=0;j<w;j++)
      {
        n1 += n2;
        n1 = ROL(n1, n2&0xf);
        n2 += 2;
        n2 = ROR(n2, 1);
        
        int val = (int)(n1*intensity)+1;
        
        LICE_pixel c = w;
        LICE_pixel c2 = w/2;
        if(i>0)
        {
          c = p[j-span];
          if(j==0)
            c2 = p[(w-1)-span];
          else
            c2 = p[(j-1)-span];
        }
        
        int pix = (((c + c2)/2) + val);
        if(pix>maxc) maxc = pix;
        p[j] = pix;
      }
      p+=span;
    }
  }

  //normalize values and apply gamma
  {
    LICE_pixel *p = startp;
    float sc=255.0f/maxc;

    for(int i=0;i<h;i++)
    {
      for(int j=0;j<w;j++)
      {
        float col = (float)fabs(p[j]*sc);
        p[j] = LICE_RGBA((int)(col*rv),(int)(col*gv),(int)(col*bv),255);
      }
      p+=span;
    }
  }
}

//standard perlin noise implementation
#if 1
int m_noiseTab[512];
void initNoise()
{
  static int init = 0;
  if(!init)
  {
    const int permutation[] = { 151,160,137,91,90,15,
      131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
      190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
      88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
      77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
      102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
      135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
      5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
      223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
      129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
      251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
      49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
      138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
    };
    for (int i=0; i < 256 ; i++) 
      m_noiseTab[256+i] = m_noiseTab[i] = permutation[i];    
    init = 1;
  }
}

static __inline float fade(float t) 
{ 
  return t * t * t * (t * (t * 6 - 15) + 10); 
}
static __inline float lerp(float t, float a, float b) 
{ 
  return a + t * (b - a); 
}
static __inline float grad(int hash, float x, float y) 
{
  //convert lo 4 bits of hash code into 12 gradient directions
  int h = hash & 15;
  float u = h<8 ? x : y,
         v = h<4 ? y : h==12||h==14 ? x : 0;
  return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
}
static float noise(float x, float y) 
{
  //find unit cube that contains point
  int X = (int)floor(x) & 255, Y = (int)floor(y) & 255;

  //find relative x,y,z of point in cube
  x -= (float)floor(x);
  y -= (float)floor(y);

  //compute fade curves for each of x,y,z
  float u = fade(x), v = fade(y);

  //hash coordinates of the 8 cube corners
  int A = m_noiseTab[X  ]+Y, AA = m_noiseTab[A], AB = m_noiseTab[A+1], 
      B = m_noiseTab[X+1]+Y, BA = m_noiseTab[B], BB = m_noiseTab[B+1];

  //and add blended results from 8 corners of cube
  return lerp(v, lerp(u, grad(m_noiseTab[AA  ], x  , y     ),
                         grad(m_noiseTab[BA  ], x-1, y     )),
                 lerp(u, grad(m_noiseTab[AB  ], x  , y-1   ),
                         grad(m_noiseTab[BB  ], x-1, y-1   )));
}
#else
//faster implementation but way lower quality
#define noiseWidth 128
#define noiseHeight 128

float m_noiseTab[noiseWidth][noiseHeight];

void initNoise()
{
  static int init = 0;
  if(init) return;

  for (int x = 0; x < noiseWidth; x++)
    for (int y = 0; y < noiseHeight; y++)
    {
      m_noiseTab[x][y] = (float)(rand() % 32768) / 32768.0;
    }

  init = 1;  
}

float noise(float x, float y)
{
  //x*=noiseWidth;
  //y*=noiseHeight;
  
  //get fractional part of x and y
  float fractX = x - (int)x;
  float fractY = y - (int)y;
  
  //wrap around
  int x1 = ((int)x + noiseWidth) % noiseWidth;
  int y1 = ((int)y + noiseHeight) % noiseHeight;
  
  //neighbor values
  int x2 = (x1 + noiseWidth - 1) % noiseWidth;
  int y2 = (y1 + noiseHeight - 1) % noiseHeight;
  
  //smooth the noise with bilinear interpolation
  float value = 0.0;
  value += fractX       * fractY       * m_noiseTab[x1][y1];
  value += fractX       * (1 - fractY) * m_noiseTab[x1][y2];
  value += (1 - fractX) * fractY       * m_noiseTab[x2][y1];
  value += (1 - fractX) * (1 - fractY) * m_noiseTab[x2][y2];
  
  return value;
}
#endif

void LICE_TexGen_Noise(LICE_IBitmap *dest, const RECT *rect, float rv, float gv, float bv, float intensity, int mode, int smooth)
{
  initNoise();

  int span=dest->getRowSpan();
  int w = dest->getWidth();
  int h = dest->getHeight();
  int dx = 0;
  int dy = 0;
  if(rect)
  {
    dx = rect->left;
    dy = rect->top;
    w = rect->right - rect->left;
    h = rect->bottom - rect->top;
  }

  if (dx<0) { w+=dx; dx=0; }
  if (dy<0) { h+=dy; dy=0; }
  const int destbm_w = dest->getWidth(), destbm_h = dest->getHeight();
  if (w<1 || h < 1 || dx >= destbm_w || dy >= destbm_h) return;

  if (w>destbm_w-dx) w=destbm_w-dx;
  if (h>destbm_h-dy) h=destbm_h-dy;

  LICE_pixel *startp = dest->getBits();
  if (dest->isFlipped())
  {
    startp += dx + (dest->getHeight()-1-dy)*span;
    span=-span;
  }
  else startp  += dx + dy*span;

  {
    LICE_pixel *p = startp;
    for(int i=0;i<h;i++)
    {
      for(int j=0;j<w;j++)
      {
        float x = (float)j/w*16*intensity;
        float y = (float)i/h*16*intensity;

        float val = 0;
        int size = smooth;
        while(size>=1)
        {
          switch(mode)
          {
          case NOISE_MODE_NORMAL: val += noise(x/size, y/size)*size; break;
          case NOISE_MODE_WOOD: val += (float)cos( x/size + noise(x/size,y/size) )*size/2; break;
          }
          size /= 2;
        }
        float col = (float)fabs(val/smooth)*255;
        if(col>255) col=255;

        p[j] = LICE_RGBA((int)(col*rv),(int)(col*gv),(int)(col*bv),255);
      }
      p+=span;
    }
  }
}

static float turbulence(int x, int y, float size, float isize)
{
  float value = 0.0;
  const float initialSize = isize;
  while(size >= 1)
  {
    value += noise(x * isize, y * isize) * size;
    size *= 0.5f;
    isize *= 2.0f;
  }
  return(128.0f * value * initialSize);
}

void LICE_TexGen_CircNoise(LICE_IBitmap *dest, const RECT *rect, float rv, float gv, float bv, float nrings, float power, int size)
{
  initNoise();

  int span=dest->getRowSpan();
  int w = dest->getWidth();
  int h = dest->getHeight();
  int x = 0;
  int y = 0;
  if(rect)
  {
    x = rect->left;
    y = rect->top;
    w = rect->right - rect->left;
    h = rect->bottom - rect->top;
  }

  if (x<0) { w+=x; x=0; }
  if (y<0) { h+=y; y=0; }
  const int destbm_w = dest->getWidth(), destbm_h = dest->getHeight();
  if (w<1 || h < 1 || x >= destbm_w || y >= destbm_h) return;

  if (w>destbm_w-x) w=destbm_w-x;
  if (h>destbm_h-y) h=destbm_h-y;

  LICE_pixel *startp = dest->getBits();
  if (dest->isFlipped())
  {
    startp += x + (dest->getHeight()-1-y)*span;
    span=-span;
  }
  else startp  += x + y*span;

  float xyPeriod = nrings;
  float turbPower = power;
  const float iturbSize = 1.0f/(float)size;
  const float turbSize = (float)size;
   
  {
    LICE_pixel *p = startp;
    for(int i=0;i<h;i++)
    {
      for(int j=0;j<w;j++)
      {
        float xValue = ((float)j - w / 2) / w;
        float yValue = ((float)i - h / 2) / h;

        float distValue = (float) (sqrt(xValue * xValue + yValue * yValue) + turbPower * turbulence(j, i, turbSize, iturbSize) / 256.0);
        float col = (float)fabs(256.0 * sin(2 * xyPeriod * distValue * 3.14159));

        p[j] = LICE_RGBA((int)(col*rv),(int)(col*bv),(int)(col*gv),255);
      }
      p+=span;
   }
  }
}
