Code

Super duper mega (fun!) commit: replaced encoding=utf-8 with fileencoding=utf-8 in...
[inkscape.git] / src / display / nr-filter-turbulence.cpp
index d0921047af972f7cf0bf4f8056af00640c9b63e8..dde92c0a57070d4145d767807271884acded0e8c 100644 (file)
@@ -2,20 +2,32 @@
  * feTurbulence filter primitive renderer
  *
  * Authors:
- *   Felipe Corrêa da Silva Sanches <felipe.sanches@gmail.com> 
+ *   World Wide Web Consortium <http://www.w3.org/>
+ *   Felipe Corrêa da Silva Sanches <juca@members.fsf.org>
  *
- * Copyright (C) 2007 authors
+ * This file has a considerable amount of code adapted from
+ *  the W3C SVG filter specs, available at:
+ *  http://www.w3.org/TR/SVG11/filters.html#feTurbulence
+ *
+ * W3C original code is licensed under the terms of
+ *  the (GPL compatible) W3C® SOFTWARE NOTICE AND LICENSE:
+ *  http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
  *
- * Released under GNU GPL, read the file 'COPYING' for more information
+ * Copyright (C) 2007 authors
+ * Released under GNU GPL version 2 (or later), read the file 'COPYING' for more information
  */
+
 #include "display/nr-arena-item.h"
 #include "display/nr-filter.h"
 #include "display/nr-filter-turbulence.h"
+#include "display/nr-filter-units.h"
 #include "display/nr-filter-utils.h"
+#include "libnr/nr-rect-l.h"
+#include "libnr/nr-blit.h"
 #include <math.h>
 
-namespace NR {
+namespace Inkscape {
+namespace Filters{
 
 FilterTurbulence::FilterTurbulence()
 : XbaseFrequency(0),
@@ -23,6 +35,7 @@ FilterTurbulence::FilterTurbulence()
   numOctaves(1),
   seed(0),
   updated(false),
+  updated_area(NR::IPoint(), NR::IPoint()),
   pix(NULL),
   fTileWidth(10), //guessed
   fTileHeight(10), //guessed
@@ -36,7 +49,12 @@ FilterPrimitive * FilterTurbulence::create() {
 }
 
 FilterTurbulence::~FilterTurbulence()
-{}
+{
+    if (pix) {
+        nr_pixblock_release(pix);
+        delete pix;
+    }
+}
 
 void FilterTurbulence::set_baseFrequency(int axis, double freq){
     if (axis==0) XbaseFrequency=freq;
@@ -63,75 +81,111 @@ void FilterTurbulence::set_updated(bool u){
     updated=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;
-    int bbox_y1 = (int) slot.get_arenaitem()->bbox.y1;
-
-    int w = bbox_x1 - bbox_x0;
-    int h = bbox_y1 - bbox_y0;    
-    int x,y;
-
-    if (!pix){
-        pix = new NRPixBlock;
-        nr_pixblock_setup_fast(pix, NR_PIXBLOCK_MODE_R8G8B8A8P, bbox_x0, bbox_y0, bbox_x1, bbox_y1, true);
-        pix_data = NR_PIXBLOCK_PX(pix);
-    }
+void FilterTurbulence::render_area(NRPixBlock *pix, NR::IRect &full_area, FilterUnits const &units) {
+    const int bbox_x0 = full_area.min()[NR::X];
+    const int bbox_y0 = full_area.min()[NR::Y];
+    const int bbox_x1 = full_area.max()[NR::X];
+    const int bbox_y1 = full_area.max()[NR::Y];
 
-    TurbulenceInit((long)seed);
+    Geom::Matrix unit_trans = units.get_matrix_primitiveunits2pb().inverse();
 
     double point[2];
 
+    unsigned char *pb = NR_PIXBLOCK_PX(pix);
+
     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 );
+        for (int y = std::max(bbox_y0, pix->area.y0); y < std::min(bbox_y1, pix->area.y1); y++){
+            int out_line = (y - pix->area.y0) * pix->rs;
+            point[1] = y * unit_trans[3] + unit_trans[5];
+            for (int x = std::max(bbox_x0, pix->area.x0); x < std::min(bbox_x1, pix->area.x1); x++){
+                int out_pos = out_line + 4 * (x - pix->area.x0);
+                point[0] = x * unit_trans[0] + unit_trans[4];
+                pb[out_pos] = CLAMP_D_TO_U8( turbulence(0,point)*255 ); // CLAMP includes rounding!
+                pb[out_pos + 1] = CLAMP_D_TO_U8( turbulence(1,point)*255 );
+                pb[out_pos + 2] = CLAMP_D_TO_U8( turbulence(2,point)*255 );
+                pb[out_pos + 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 );
+        for (int y = std::max(bbox_y0, pix->area.y0); y < std::min(bbox_y1, pix->area.y1); y++){
+            int out_line = (y - pix->area.y0) * pix->rs;
+            point[1] = y * unit_trans[3] + unit_trans[5];
+            for (int x = std::max(bbox_x0, pix->area.x0); x < std::min(bbox_x1, pix->area.x1); x++){
+                int out_pos = out_line + 4 * (x - pix->area.x0);
+                point[0] = x * unit_trans[0] + unit_trans[4];
+                pb[out_pos] = CLAMP_D_TO_U8( ((turbulence(0,point)*255) +255)/2 );
+                pb[out_pos + 1] = CLAMP_D_TO_U8( ((turbulence(1,point)*255)+255)/2 );
+                pb[out_pos + 2] = CLAMP_D_TO_U8( ((turbulence(2,point)*255) +255)/2 );
+                pb[out_pos + 3] = CLAMP_D_TO_U8( ((turbulence(3,point)*255) +255)/2 );
             }
         }
     }
-    updated=true;
+
+    pix->empty = FALSE;
 }
 
-int FilterTurbulence::render(FilterSlot &slot, Matrix const &trans) {
-//g_warning("render");
-    if (!updated) update_pixbuffer(slot);
+void FilterTurbulence::update_pixbuffer(NR::IRect &area, FilterUnits const &units) {
+    int bbox_x0 = area.min()[NR::X];
+    int bbox_y0 = area.min()[NR::Y];
+    int bbox_x1 = area.max()[NR::X];
+    int bbox_y1 = area.max()[NR::Y];
+
+    TurbulenceInit((long)seed);
+
+    if (!pix){
+        pix = new NRPixBlock;
+        nr_pixblock_setup_fast(pix, NR_PIXBLOCK_MODE_R8G8B8A8N, bbox_x0, bbox_y0, bbox_x1, bbox_y1, true);
+    }
+    else if (bbox_x0 != pix->area.x0 || bbox_y0 != pix->area.y0 ||
+        bbox_x1 != pix->area.x1 || bbox_y1 != pix->area.y1)
+    {
+        /* TODO: release-setup cycle not actually needed, if pixblock
+         * width and height don't change */
+        nr_pixblock_release(pix);
+        nr_pixblock_setup_fast(pix, NR_PIXBLOCK_MODE_R8G8B8A8N, bbox_x0, bbox_y0, bbox_x1, bbox_y1, true);
+    }
+
+    /* This limits pre-rendered turbulence to two megapixels. This is
+     * arbitary limit and could be something other, too.
+     * If bigger area is needed, visible area is rendered on demand. */
+    if (!pix || (pix->size != NR_PIXBLOCK_SIZE_TINY && pix->data.px == NULL) ||
+        ((bbox_x1 - bbox_x0) * (bbox_y1 - bbox_y0) > 2*1024*1024)) {
+        pix_data = NULL;
+        return;
+    }
+
+    render_area(pix, area, units);
+
+    pix_data = NR_PIXBLOCK_PX(pix);
     
+    updated=true;
+    updated_area = area;
+}
+
+int FilterTurbulence::render(FilterSlot &slot, FilterUnits const &units) {
+    NR::IRect area = units.get_pixblock_filterarea_paraller();
+    // TODO: could be faster - updated_area only has to be same size as area
+    if (!updated || updated_area != area) update_pixbuffer(area, units);
+
     NRPixBlock *in = slot.get(_input);
+    if (!in) {
+        g_warning("Missing source image for feTurbulence (in=%d)", _input);
+        return 1;
+    }
+
     NRPixBlock *out = new NRPixBlock;
-    int x,y;
     int x0 = in->area.x0, y0 = in->area.y0;
     int x1 = in->area.x1, y1 = in->area.y1;
-    int w = x1 - x0;
-    nr_pixblock_setup_fast(out, in->mode, x0, y0, x1, y1, true);
+    nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8N, x0, y0, x1, y1, true);
 
-    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;
-    int bbox_w = bbox_x1 - bbox_x0;
-    
-    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[4*(x - bbox_x0 + bbox_w*(y - bbox_y0)) ];
-            out_data[4*((x - x0)+w*(y - y0)) + 1] = pix_data[4*(x - bbox_x0 + bbox_w*(y - bbox_y0))+1];
-            out_data[4*((x - x0)+w*(y - y0)) + 2] = pix_data[4*(x - bbox_x0 + bbox_w*(y - bbox_y0))+2];
-            out_data[4*((x - x0)+w*(y - y0)) + 3] = pix_data[4*(x - bbox_x0 + bbox_w*(y - bbox_y0))+3];
-        }
+    if (pix_data) {
+        /* If pre-rendered output of whole filter area exists, just copy it. */
+        nr_blit_pixblock_pixblock(out, pix);
+    } else {
+        /* No pre-rendered output, render the required area here. */
+        render_area(out, area, units);
     }
+
     out->empty = FALSE;
     slot.set(_output, out);
     return 0;
@@ -154,7 +208,6 @@ long FilterTurbulence::TurbulenceRandom(long lSeed)
 
 void FilterTurbulence::TurbulenceInit(long lSeed)
 {
-g_warning("init");
   double s;
   int i, j, k;
   lSeed = Turbulence_setup_seed(lSeed);
@@ -235,7 +288,6 @@ double FilterTurbulence::TurbulenceNoise2(int nColorChannel, double vec[2], Stit
 
 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.
@@ -295,7 +347,12 @@ double FilterTurbulence::turbulence(int nColorChannel, double *point)
   return fSum;
 }
 
-} /* namespace NR */
+FilterTraits FilterTurbulence::get_input_traits() {
+    return TRAIT_PARALLER;
+}
+
+} /* namespace Filters */
+} /* namespace Inkscape */
 
 /*
   Local Variables:
@@ -306,4 +363,4 @@ double FilterTurbulence::turbulence(int nColorChannel, double *point)
   fill-column:99
   End:
 */
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :