Code

first implementation of feTurbulence renderer
authorjucablues <jucablues@users.sourceforge.net>
Thu, 16 Aug 2007 23:51:13 +0000 (23:51 +0000)
committerjucablues <jucablues@users.sourceforge.net>
Thu, 16 Aug 2007 23:51:13 +0000 (23:51 +0000)
src/display/nr-filter-turbulence.cpp
src/display/nr-filter-turbulence.h

index f94081612d36b2cbd75cc93a295bd6b05b1d99ba..8796090091f5fefe571b921751744ab03d7299e6 100644 (file)
@@ -12,6 +12,8 @@
 #include "display/nr-arena-item.h"
 #include "display/nr-filter.h"
 #include "display/nr-filter-turbulence.h"
+#include "display/nr-filter-utils.h"
+#include <math.h>
 
 namespace NR {
 
@@ -21,7 +23,11 @@ FilterTurbulence::FilterTurbulence()
   numOctaves(1),
   seed(0),
   updated(false),
-  pix(NULL)
+  pix(NULL),
+  fTileWidth(10), //guessed
+  fTileHeight(10), //guessed
+  fTileX(1), //guessed
+  fTileY(1) //guessed
 {
 }
 
@@ -58,6 +64,7 @@ void FilterTurbulence::set_updated(bool u){
 }
 
 void FilterTurbulence::update_pixbuffer(FilterSlot &slot) {
+//g_warning("update_pixbuf");
     int bbox_x0 = (int) slot.get_arenaitem()->bbox.x0;
     int bbox_y0 = (int) slot.get_arenaitem()->bbox.y0;
     int bbox_x1 = (int) slot.get_arenaitem()->bbox.x1;
@@ -72,30 +79,35 @@ void FilterTurbulence::update_pixbuffer(FilterSlot &slot) {
         nr_pixblock_setup_fast(pix, NR_PIXBLOCK_MODE_R8G8B8A8P, bbox_x0, bbox_y0, bbox_x1, bbox_y1, true);
         pix_data = NR_PIXBLOCK_PX(pix);
     }
-    
-//  TODO: implement here the turbulence rendering.
-
-/*debug: these are the available parameters
-    printf("XbaseFrequency = %f; ", XbaseFrequency);
-    printf("YbaseFrequency = %f; ", YbaseFrequency);
-    printf("numOctaves = %d;\n", numOctaves);
-    printf("seed = %f; ", seed);
-    printf("stitchTiles = %s; ", stitchTiles ? "stitch" : "noStitch");
-    printf("type = %s;\n\n", type==0 ? "FractalNoise" : "turbulence");
-*/
 
-    for (x=0; x < w; x++){
-        for (y=0; y < h; y++){
-            pix_data[4*(x + w*y)] = (unsigned char)(int(XbaseFrequency)%256);
-            pix_data[4*(x + w*y) + 1] = (unsigned char)(int(YbaseFrequency)%256);
-            pix_data[4*(x + w*y) + 2] = (unsigned char)(int(numOctaves)%256);
-            pix_data[4*(x + w*y) + 3] = (unsigned char)(int(seed)%256);
+    TurbulenceInit((long)seed);
+
+    double point[2];
+
+    if (type==TURBULENCE_TURBULENCE){
+        for (point[0]=0; point[0] < w; point[0]++){
+            for (point[1]=0; point[1] < h; point[1]++){
+                pix_data[4*(int(point[0]) + w*int(point[1]))] = CLAMP_D_TO_U8( turbulence(0,point)*255 );
+                pix_data[4*(int(point[0]) + w*int(point[1])) + 1] = CLAMP_D_TO_U8( turbulence(1,point)*255 );
+                pix_data[4*(int(point[0]) + w*int(point[1])) + 2] = CLAMP_D_TO_U8( turbulence(2,point)*255 );
+                pix_data[4*(int(point[0]) + w*int(point[1])) + 3] = CLAMP_D_TO_U8( turbulence(3,point)*255 );
+            }
+        }
+    } else {
+        for (point[0]=0; point[0] < w; point[0]++){
+            for (point[1]=0; point[1] < h; point[1]++){
+                pix_data[4*(int(point[0]) + w*int(point[1]))] = CLAMP_D_TO_U8( ((turbulence(0,point)*255) +255)/2 );
+                pix_data[4*(int(point[0]) + w*int(point[1])) + 1] = CLAMP_D_TO_U8( ((turbulence(1,point)*255)+255)/2 );
+                pix_data[4*(int(point[0]) + w*int(point[1])) + 2] = CLAMP_D_TO_U8( ((turbulence(2,point)*255) +255)/2 );
+                pix_data[4*(int(point[0]) + w*int(point[1])) + 3] = CLAMP_D_TO_U8( ((turbulence(3,point)*255) +255)/2 );
+            }
         }
     }
     updated=true;
 }
 
 int FilterTurbulence::render(FilterSlot &slot, Matrix const &trans) {
+//g_warning("render");
     if (!updated) update_pixbuffer(slot);
     
     NRPixBlock *in = slot.get(_input);
@@ -112,18 +124,175 @@ int FilterTurbulence::render(FilterSlot &slot, Matrix const &trans) {
     unsigned char *out_data = NR_PIXBLOCK_PX(out);
     for (x=x0; x < x1; x++){
         for (y=y0; y < y1; y++){
-            out_data[4*((x - x0)+w*(y - y0))] = pix_data[x - bbox_x0 + w*(y - bbox_y0)];
-            out_data[4*((x - x0)+w*(y - y0)) + 1] = pix_data[x - bbox_x0 + w*(y - bbox_y0)];
-            out_data[4*((x - x0)+w*(y - y0)) + 2] = pix_data[x - bbox_x0 + w*(y - bbox_y0)];
-            out_data[4*((x - x0)+w*(y - y0)) + 3] = pix_data[x - bbox_x0 + w*(y - bbox_y0)];
+            out_data[4*((x - x0)+w*(y - y0))] = pix_data[4*(x - bbox_x0 + w*(y - bbox_y0)) ];
+            out_data[4*((x - x0)+w*(y - y0)) + 1] = pix_data[4*(x - bbox_x0 + w*(y - bbox_y0))+1];
+            out_data[4*((x - x0)+w*(y - y0)) + 2] = pix_data[4*(x - bbox_x0 + w*(y - bbox_y0))+2];
+            out_data[4*((x - x0)+w*(y - y0)) + 3] = pix_data[4*(x - bbox_x0 + w*(y - bbox_y0))+3];
         }
     }
-
     out->empty = FALSE;
     slot.set(_output, out);
     return 0;
 }
 
+long FilterTurbulence::Turbulence_setup_seed(long lSeed)
+{
+  if (lSeed <= 0) lSeed = -(lSeed % (RAND_m - 1)) + 1;
+  if (lSeed > RAND_m - 1) lSeed = RAND_m - 1;
+  return lSeed;
+}
+
+long FilterTurbulence::TurbulenceRandom(long lSeed)
+{
+  long result;
+  result = RAND_a * (lSeed % RAND_q) - RAND_r * (lSeed / RAND_q);
+  if (result <= 0) result += RAND_m;
+  return result;
+}
+
+void FilterTurbulence::TurbulenceInit(long lSeed)
+{
+g_warning("init");
+  double s;
+  int i, j, k;
+  lSeed = Turbulence_setup_seed(lSeed);
+  for(k = 0; k < 4; k++)
+  {
+    for(i = 0; i < BSize; i++)
+    {
+      uLatticeSelector[i] = i;
+      for (j = 0; j < 2; j++)
+        fGradient[k][i][j] = (double)(((lSeed = TurbulenceRandom(lSeed)) % (BSize + BSize)) - BSize) / BSize;
+      s = double(sqrt(fGradient[k][i][0] * fGradient[k][i][0] + fGradient[k][i][1] * fGradient[k][i][1]));
+      fGradient[k][i][0] /= s;
+      fGradient[k][i][1] /= s;
+    }
+  }
+  while(--i)
+  {
+    k = uLatticeSelector[i];
+    uLatticeSelector[i] = uLatticeSelector[j = (lSeed = TurbulenceRandom(lSeed)) % BSize];
+    uLatticeSelector[j] = k;
+  }
+  for(i = 0; i < BSize + 2; i++)
+  {
+    uLatticeSelector[BSize + i] = uLatticeSelector[i];
+    for(k = 0; k < 4; k++)
+      for(j = 0; j < 2; j++)
+        fGradient[k][BSize + i][j] = fGradient[k][i][j];
+  }
+}
+
+double FilterTurbulence::TurbulenceNoise2(int nColorChannel, double vec[2], StitchInfo *pStitchInfo)
+{
+  int bx0, bx1, by0, by1, b00, b10, b01, b11;
+  double rx0, rx1, ry0, ry1, *q, sx, sy, a, b, t, u, v;
+  int i, j;
+  t = vec[0] + PerlinN;
+  bx0 = (int)t;
+  bx1 = bx0+1;
+  rx0 = t - (int)t;
+  rx1 = rx0 - 1.0f;
+  t = vec[1] + PerlinN;
+  by0 = (int)t;
+  by1 = by0+1;
+  ry0 = t - (int)t;
+  ry1 = ry0 - 1.0f;
+  // If stitching, adjust lattice points accordingly.
+  if(pStitchInfo != NULL)
+  {
+    if(bx0 >= pStitchInfo->nWrapX)
+      bx0 -= pStitchInfo->nWidth;
+    if(bx1 >= pStitchInfo->nWrapX)
+      bx1 -= pStitchInfo->nWidth;
+    if(by0 >= pStitchInfo->nWrapY)
+      by0 -= pStitchInfo->nHeight;
+    if(by1 >= pStitchInfo->nWrapY)
+      by1 -= pStitchInfo->nHeight;
+  }
+  bx0 &= BM;
+  bx1 &= BM;
+  by0 &= BM;
+  by1 &= BM;
+  i = uLatticeSelector[bx0];
+  j = uLatticeSelector[bx1];
+  b00 = uLatticeSelector[i + by0];
+  b10 = uLatticeSelector[j + by0];
+  b01 = uLatticeSelector[i + by1];
+  b11 = uLatticeSelector[j + by1];
+  sx = double(s_curve(rx0));
+  sy = double(s_curve(ry0));
+  q = fGradient[nColorChannel][b00]; u = rx0 * q[0] + ry0 * q[1];
+  q = fGradient[nColorChannel][b10]; v = rx1 * q[0] + ry0 * q[1];
+  a = turb_lerp(sx, u, v);
+  q = fGradient[nColorChannel][b01]; u = rx0 * q[0] + ry1 * q[1];
+  q = fGradient[nColorChannel][b11]; v = rx1 * q[0] + ry1 * q[1];
+  b = turb_lerp(sx, u, v);
+  return turb_lerp(sy, a, b);
+}
+
+double FilterTurbulence::turbulence(int nColorChannel, double *point)
+{
+//g_warning("turbulence");
+  StitchInfo stitch;
+  StitchInfo *pStitchInfo = NULL; // Not stitching when NULL.
+  // Adjust the base frequencies if necessary for stitching.
+  if(stitchTiles)
+  {
+    // When stitching tiled turbulence, the frequencies must be adjusted
+    // so that the tile borders will be continuous.
+    if(XbaseFrequency != 0.0)
+    {
+      double fLoFreq = double(floor(fTileWidth * XbaseFrequency)) / fTileWidth;
+      double fHiFreq = double(ceil(fTileWidth * XbaseFrequency)) / fTileWidth;
+      if(XbaseFrequency / fLoFreq < fHiFreq / XbaseFrequency)
+        XbaseFrequency = fLoFreq;
+      else
+        XbaseFrequency = fHiFreq;
+    }
+    if(YbaseFrequency != 0.0)
+    {
+      double fLoFreq = double(floor(fTileHeight * YbaseFrequency)) / fTileHeight;
+      double fHiFreq = double(ceil(fTileHeight * YbaseFrequency)) / fTileHeight;
+      if(YbaseFrequency / fLoFreq < fHiFreq / YbaseFrequency)
+        YbaseFrequency = fLoFreq;
+      else
+        YbaseFrequency = fHiFreq;
+    }
+    // Set up TurbulenceInitial stitch values.
+    pStitchInfo = &stitch;
+    stitch.nWidth = int(fTileWidth * XbaseFrequency + 0.5f);
+    stitch.nWrapX = int(fTileX * XbaseFrequency + PerlinN + stitch.nWidth);
+    stitch.nHeight = int(fTileHeight * YbaseFrequency + 0.5f);
+    stitch.nWrapY = int(fTileY * YbaseFrequency + PerlinN + stitch.nHeight);
+  }
+  double fSum = 0.0f;
+  double vec[2];
+  vec[0] = point[0] * XbaseFrequency;
+  vec[1] = point[1] * YbaseFrequency;
+  double ratio = 1;
+  for(int nOctave = 0; nOctave < numOctaves; nOctave++)
+  {
+    if(type==TURBULENCE_FRACTALNOISE)
+      fSum += double(TurbulenceNoise2(nColorChannel, vec, pStitchInfo) / ratio);
+    else
+      fSum += double(fabs(TurbulenceNoise2(nColorChannel, vec, pStitchInfo)) / ratio);
+    vec[0] *= 2;
+    vec[1] *= 2;
+    ratio *= 2;
+    if(pStitchInfo != NULL)
+    {
+      // Update stitch values. Subtracting PerlinN before the multiplication and
+      // adding it afterward simplifies to subtracting it once.
+      stitch.nWidth *= 2;
+      stitch.nWrapX = 2 * stitch.nWrapX - PerlinN;
+      stitch.nHeight *= 2;
+      stitch.nWrapY = 2 * stitch.nWrapY - PerlinN;
+    }
+  }
+  return fSum;
+}
+
 } /* namespace NR */
 
 /*
index 1e2171e83cda2152165ecc600bc766b596e0b6e2..db2b2854aec6d9ac2056881343d03c23e96c34fa 100644 (file)
@@ -24,6 +24,33 @@ enum FilterTurbulenceType {
     TURBULENCE_ENDTYPE
 };
 
+struct StitchInfo
+{
+  int nWidth; // How much to subtract to wrap for stitching.
+  int nHeight;
+  int nWrapX; // Minimum value to wrap.
+  int nWrapY;
+};
+
+/* Produces results in the range [1, 2**31 - 2].
+Algorithm is: r = (a * r) mod m
+where a = 16807 and m = 2**31 - 1 = 2147483647
+See [Park & Miller], CACM vol. 31 no. 10 p. 1195, Oct. 1988
+To test: the algorithm should produce the result 1043618065
+as the 10,000th generated number if the original seed is 1.
+*/
+#define RAND_m 2147483647 /* 2**31 - 1 */
+#define RAND_a 16807 /* 7**5; primitive root of m */
+#define RAND_q 127773 /* m / a */
+#define RAND_r 2836 /* m % a */
+#define BSize 0x100
+#define BM 0xff
+#define PerlinN 0x1000
+#define NP 12 /* 2^PerlinN */
+#define NM 0xfff
+#define s_curve(t) ( t * t * (3. - 2. * t) )
+#define turb_lerp(t, a, b) ( a + t * (b - a) )
+
 class FilterTurbulence : public FilterPrimitive {
 public:
     FilterTurbulence();
@@ -40,6 +67,13 @@ public:
     virtual void set_type(FilterTurbulenceType t);
     virtual void set_updated(bool u);
 private:
+
+    long Turbulence_setup_seed(long lSeed);
+    long TurbulenceRandom(long lSeed);
+    void TurbulenceInit(long lSeed);
+    double TurbulenceNoise2(int nColorChannel, double vec[2], StitchInfo *pStitchInfo);
+    double turbulence(int nColorChannel, double *point);
+
     double XbaseFrequency, YbaseFrequency;
     int numOctaves;
     double seed;
@@ -48,6 +82,15 @@ private:
     bool updated;
     NRPixBlock *pix;
     unsigned char *pix_data;
+
+    int uLatticeSelector[BSize + BSize + 2];
+    double fGradient[4][BSize + BSize + 2][2];
+
+    double fTileWidth;
+    double fTileHeight;
+
+    double fTileX;
+    double fTileY;
 };
 
 } /* namespace NR */