[gegl/soc-2013-opecl-ops] operations: add mosaic.c



commit 741b645a7cf66cc454b4fcf4c9aa4bcb95cec940
Author: Téo Mazars <teo mazars ensimag fr>
Date:   Sat Sep 21 18:39:18 2013 +0200

    operations: add mosaic.c

 operations/common/mosaic.c | 2422 ++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 2422 insertions(+), 0 deletions(-)
---
diff --git a/operations/common/mosaic.c b/operations/common/mosaic.c
new file mode 100644
index 0000000..59c522a
--- /dev/null
+++ b/operations/common/mosaic.c
@@ -0,0 +1,2422 @@
+/* This file is an image processing operation for GEGL
+ *
+ * GEGL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * GEGL 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 1996 Spencer Kimball
+ * Copyright 1997 Elliot Lee
+ * Copyright 2013 Téo Mazars <teo mazars ensimag fr>
+ */
+
+/*  Mosaic is a filter which transforms an image into
+ *  what appears to be a mosaic, composed of small primitives,
+ *  each of constant color and of an approximate size.
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+#ifdef GEGL_CHANT_PROPERTIES
+
+gegl_chant_double_ui (tile_size, _("Tile size"), 1.0, 1000.0, 15.0,
+                      5.0, 400, 1.0, _("Tile size"))
+gegl_chant_double_ui (tile_height, _("Tile height"), 1.0, 1000.0, 4.0,
+                      1.0, 20.0, 1.0, _("Tile height"))
+gegl_chant_double_ui (tile_spacing, _("Tile spacing"), 0.1, 1000.0, 1.0,
+                      0.5, 30.0, 1.0,  _("Tile spacing"))
+gegl_chant_double    (tile_neatness, _("Tile neatness"), 0.0, 1.0, 0.65,
+                      _("Tile neatness"))
+gegl_chant_boolean   (tile_allow_split, _("Allow splitting tiles"), TRUE,
+                      _("Allow splitting tiles"))
+gegl_chant_double    (light_dir,  _("Light direction"),
+                      0.0, 360.0, 135, _("Light direction"))
+gegl_chant_double    (color_variation, _("Color variation"),
+                      0.0, 1.0, 0.2, _("Color variation"))
+gegl_chant_int       (seed, _("Random seed"), 0, G_MAXINT, 1, _("Random seed"))
+gegl_chant_boolean   (antialiasing, _("Antialiasing"), TRUE, _("Antialiasing"))
+gegl_chant_boolean   (color_averaging, _("Color averaging"), TRUE,
+                      _("Color averaging"))
+
+gegl_chant_register_enum (gegl_mosaic_tile)
+  enum_value (GEGL_MOSAIC_TILE_SQUARES,   "SQUARES")
+  enum_value (GEGL_MOSAIC_TILE_HEXAGONS,  "HEXAGONS")
+  enum_value (GEGL_MOSAIC_TILE_OCTAGONS,  "OCTAGONS")
+  enum_value (GEGL_MOSAIC_TILE_TRIANGLES, "TRIANGLES")
+gegl_chant_register_enum_end (GeglMosaicTile)
+
+gegl_chant_enum      (tile_type, _("Tile shape"), GeglMosaicTile,
+                      gegl_mosaic_tile, GEGL_MOSAIC_TILE_HEXAGONS,
+                      _("What shape to use for tiles"))
+
+gegl_chant_boolean   (tile_surface, _("Rough tile surface"), FALSE,
+                       _("Rough tile surface"))
+
+gegl_chant_color     (bg_color, _("Joins color"), "black", _("Joins color"))
+gegl_chant_color     (fg_color, _("Light color"), "white", _("Light color"))
+
+#else
+
+#define GEGL_CHANT_TYPE_AREA_FILTER
+#define GEGL_CHANT_C_FILE "mosaic.c"
+
+#include "gegl-chant.h"
+#include <math.h>
+
+#define SUPERSAMPLE       3
+#define MAG_THRESHOLD     (7.5/255.0)
+#define COUNT_THRESHOLD   0.1
+#define MAX_POINTS        12
+#define NB_CPN            4
+#define STD_DEV           1.0
+#define EPSILON           1e-5
+
+typedef enum
+{
+  SQUARES   = 0,
+  HEXAGONS  = 1,
+  OCTAGONS  = 2,
+  TRIANGLES = 3
+} TileType;
+
+typedef enum
+{
+  HORIZONTAL   = 0,
+  VERTICAL     = 1
+} Direction;
+
+#define SMOOTH   FALSE
+#define ROUGH    TRUE
+
+typedef struct
+{
+  gdouble x, y;
+} Vertex;
+
+typedef struct
+{
+  guint  npts;
+  Vertex pts[MAX_POINTS];
+} Polygon;
+
+typedef struct
+{
+  gdouble base_x, base_y;
+  gdouble norm_x, norm_y;
+  gdouble light;
+} SpecVec;
+
+typedef struct
+{
+  Vertex *vert, *vert_o;
+  gint    vert_rows;
+  gint    vert_cols;
+  gint    row_pad;
+  gint    col_pad;
+  gint    vert_multiple;
+  gint    vert_rowstride;
+} GridDescriptor;
+
+typedef struct
+{
+  gdouble        light_x;
+  gdouble        light_y;
+  gdouble        scale;
+  gfloat        *h_grad;
+  gfloat        *v_grad;
+  gfloat        *m_grad;
+  GridDescriptor grid;
+  gfloat         back[4];
+  gfloat         fore[4];
+  SpecVec        vecs[MAX_POINTS];
+  gint           width, height;
+} MosaicDatas;
+
+
+/* Declare local functions.
+ */
+
+static gfloat*   mosaic                (GeglOperation       *operation,
+                                        GeglBuffer          *drawable,
+                                        const GeglRectangle *result);
+
+/*  gradient finding machinery  */
+static void      find_gradients        (gfloat              *input_buf,
+                                        gdouble              std_dev,
+                                        const GeglRectangle *result,
+                                        MosaicDatas         *mdatas);
+static void      find_max_gradient     (gfloat              *src_rgn,
+                                        gfloat              *dest_rgn,
+                                        gint                 width,
+                                        gint                 height);
+
+/*  gaussian & 1st derivative  */
+static void      gaussian_deriv        (gfloat              *src_rgn,
+                                        gfloat              *dest_rgn,
+                                        Direction            direction,
+                                        gdouble              std_dev,
+                                        const GeglRectangle *result);
+static void      make_curve            (gfloat              *curve,
+                                        gfloat              *sum,
+                                        gdouble              std_dev,
+                                        gint                 length);
+static void      make_curve_d          (gfloat              *curve,
+                                        gfloat              *sum,
+                                        gdouble              std_dev,
+                                        gint                 length);
+
+/*  grid creation and localization machinery  */
+static void      grid_create_squares   (const GeglRectangle *result,
+                                        gdouble              tile_size,
+                                        GridDescriptor      *grid);
+static void      grid_create_hexagons  (const GeglRectangle *result,
+                                        gdouble              tile_size,
+                                        GridDescriptor      *grid);
+static void      grid_create_octagons  (const GeglRectangle *result,
+                                        gdouble              tile_size,
+                                        GridDescriptor      *grid);
+static void      grid_create_triangles (const GeglRectangle *result,
+                                        gdouble              tile_size,
+                                        GridDescriptor      *grid);
+static void      grid_localize         (const GeglRectangle *result,
+                                        GeglChantO          *o,
+                                        MosaicDatas         *mdatas);
+static gfloat*   grid_render           (gfloat              *input_buf,
+                                        const GeglRectangle *result,
+                                        GeglChantO          *o,
+                                        MosaicDatas         *mdatas);
+static void      split_poly            (Polygon             *poly,
+                                        gfloat              *input_buf,
+                                        gfloat              *output_buf,
+                                        gfloat              *col,
+                                        gdouble             *dir,
+                                        gdouble              color_vary,
+                                        const GeglRectangle *result,
+                                        GeglChantO          *o,
+                                        MosaicDatas         *mdatas);
+static void      clip_poly             (gdouble             *vec,
+                                        gdouble             *pt,
+                                        Polygon             *poly,
+                                        Polygon             *new_poly);
+static void      clip_point            (gdouble             *dir,
+                                        gdouble             *pt,
+                                        gdouble              x1,
+                                        gdouble              y1,
+                                        gdouble              x2,
+                                        gdouble              y2,
+                                        Polygon             *poly);
+static void      process_poly          (Polygon             *poly,
+                                        gboolean             allow_split,
+                                        gfloat              *input_buf,
+                                        gfloat              *output_buf,
+                                        gfloat              *col,
+                                        const GeglRectangle *result,
+                                        GeglChantO          *o,
+                                        MosaicDatas         *mdatas);
+static void      render_poly           (Polygon             *poly,
+                                        gfloat              *input_buf,
+                                        gfloat              *output_buf,
+                                        gfloat              *col,
+                                        gdouble              vary,
+                                        const GeglRectangle *result,
+                                        GeglChantO          *o,
+                                        MosaicDatas         *mdatas);
+static void      find_poly_dir         (Polygon             *poly,
+                                        gfloat              *m_gr,
+                                        gfloat              *h_gr,
+                                        gfloat              *v_gr,
+                                        gdouble             *dir,
+                                        gdouble             *loc,
+                                        const GeglRectangle *result);
+static void      find_poly_color       (Polygon             *poly,
+                                        gfloat              *input_buf,
+                                        gfloat              *col,
+                                        double               vary,
+                                        const GeglRectangle *result);
+static void      scale_poly            (Polygon             *poly,
+                                        gdouble              cx,
+                                        gdouble              cy,
+                                        gdouble              scale);
+static void      fill_poly_color       (Polygon             *poly,
+                                        gfloat              *input_buf,
+                                        gfloat              *output_buf,
+                                        gfloat              *col,
+                                        const GeglRectangle *result,
+                                        gboolean             antialiasing,
+                                        gboolean             tile_rough,
+                                        gdouble              tile_height,
+                                        MosaicDatas         *mdatas);
+static void      fill_poly_image       (Polygon             *poly,
+                                        gfloat              *input_buf,
+                                        gfloat              *output_buf,
+                                        gdouble             vary,
+                                        const GeglRectangle *result,
+                                        gboolean             antialiasing,
+                                        gboolean             tile_rough,
+                                        gdouble              tile_height,
+                                        MosaicDatas         *mdatas);
+static void      calc_spec_vec         (SpecVec             *vec,
+                                        gint                 xs,
+                                        gint                 ys,
+                                        gint                 xe,
+                                        gint                 ye,
+                                        gdouble              light_x,
+                                        gdouble              light_y);
+static gdouble   calc_spec_contrib     (SpecVec             *vec,
+                                        gint                 n,
+                                        gdouble              x,
+                                        gdouble              y,
+                                        gboolean             tile_rough,
+                                        gdouble              tile_height);
+/*  Polygon machinery  */
+static void      convert_segment       (gint                 x1,
+                                        gint                 y1,
+                                        gint                 x2,
+                                        gint                 y2,
+                                        gint                 offset,
+                                        gint                *min,
+                                        gint                *max);
+static void      polygon_add_point     (Polygon             *poly,
+                                        gdouble              x,
+                                        gdouble              y);
+static gboolean  polygon_find_center   (Polygon             *poly,
+                                        gdouble             *x,
+                                        gdouble             *y);
+static void      polygon_translate     (Polygon             *poly,
+                                        gdouble              tx,
+                                        gdouble              ty);
+static void      polygon_scale         (Polygon             *poly,
+                                        gdouble              scale);
+static gboolean  polygon_extents       (Polygon             *poly,
+                                        gdouble             *min_x,
+                                        gdouble             *min_y,
+                                        gdouble             *max_x,
+                                        gdouble             *max_y);
+static void      polygon_reset         (Polygon             *poly);
+static gfloat    rand_f                (gint                 seed,
+                                        gfloat               pos_x,
+                                        gfloat               pos_y,
+                                        gfloat               min,
+                                        gfloat               max);
+static gint      rand_i                (gint                 seed,
+                                        gfloat               pos_x,
+                                        gfloat               pos_y,
+                                        gint                 min,
+                                        gint                 max);
+
+#define ROUND(x)   ((gint) ((((x) < 0) ? (x) - 0.5 : (x) + 0.5)))
+
+#define CLAMP01(v) ((v) < 0.0 ? 0.0 : ((v) > 1.0 ? 1.0 : (v)))
+
+#define SQR(v)     ((v)*(v))
+
+static gfloat
+rand_f (gint   seed,
+        gfloat pos_x,
+        gfloat pos_y,
+        gfloat min,
+        gfloat max)
+{
+  return gegl_random_float_range (seed, ROUND (pos_x), ROUND (pos_y),
+                                  0, 0, min, max);
+}
+
+static gint
+rand_i (gint   seed,
+        gfloat pos_x,
+        gfloat pos_y,
+        gint   min,
+        gint   max)
+{
+  return gegl_random_int_range (seed, ROUND (pos_x), ROUND (pos_y),
+                                0, 0, min, max);
+}
+
+static gfloat*
+mosaic (GeglOperation       *operation,
+        GeglBuffer          *drawable,
+        const GeglRectangle *result)
+{
+  GeglChantO          *o;
+  MosaicDatas          mdatas;
+  gfloat              *rendered;
+  const GeglRectangle *whole_region;
+  gfloat              *input_buf;
+
+  o = GEGL_CHANT_PROPERTIES (operation);
+
+  input_buf = g_new (gfloat, NB_CPN * result->width * result->height);
+
+  gegl_buffer_get (drawable, result,
+                   1.0, babl_format ("R'G'B'A float"), input_buf,
+                   GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+  {
+    gdouble r, g, b, a;
+    gegl_color_get_rgba (o->fg_color, &r, &g, &b, &a);
+    mdatas.fore[0] = r;
+    mdatas.fore[1] = g;
+    mdatas.fore[2] = b;
+    mdatas.fore[3] = a;
+
+    gegl_color_get_rgba (o->bg_color, &r, &g, &b, &a);
+    mdatas.back[0] = r;
+    mdatas.back[1] = g;
+    mdatas.back[2] = b;
+    mdatas.back[3] = a;
+  }
+
+  whole_region = gegl_operation_source_get_bounding_box (operation, "input");
+
+  mdatas.width  = whole_region->width;
+  mdatas.height = whole_region->height;
+
+  /*  Find the gradients  */
+  find_gradients (input_buf, STD_DEV, result, &mdatas);
+
+  /*  Create the tile geometry grid  */
+  switch (o->tile_type)
+    {
+    case SQUARES:
+      grid_create_squares (result, o->tile_size, &(mdatas.grid));
+      break;
+    case HEXAGONS:
+      grid_create_hexagons (result, o->tile_size, &(mdatas.grid));
+      break;
+    case OCTAGONS:
+      grid_create_octagons (result, o->tile_size, &(mdatas.grid));
+      break;
+    case TRIANGLES:
+      grid_create_triangles (result, o->tile_size, &(mdatas.grid));
+      break;
+    default:
+      break;
+    }
+
+  /*  Deform the tiles based on image content  */
+  grid_localize (result, o, &mdatas);
+
+  mdatas.light_x = -cos (o->light_dir * G_PI / 180.0);
+  mdatas.light_y =  sin (o->light_dir * G_PI / 180.0);
+  mdatas.scale = (o->tile_spacing > o->tile_size / 2.0) ?
+    0.5 : 1.0 - o->tile_spacing / o->tile_size;
+
+  /*  Render the tiles  */
+  rendered = grid_render (input_buf, result, o, &mdatas);
+
+  g_free (mdatas.h_grad);
+  g_free (mdatas.v_grad);
+  g_free (mdatas.m_grad);
+  g_free (mdatas.grid.vert_o);
+  g_free (input_buf);
+
+  return rendered;
+}
+
+
+/*
+ *  Gradient finding machinery
+ */
+
+static void
+find_gradients (gfloat              *input_buf,
+                gdouble              std_dev,
+                const GeglRectangle *result,
+                MosaicDatas         *mdatas)
+{
+  gfloat *dest_rgn;
+  gint    i, j;
+  gfloat *gr, *dh, *dv;
+  gfloat  hmax, vmax;
+
+  /*  allocate the gradient maps  */
+  mdatas->h_grad = g_new (gfloat, result->width * result->height);
+  mdatas->v_grad = g_new (gfloat, result->width * result->height);
+  mdatas->m_grad = g_new (gfloat, result->width * result->height);
+
+  dest_rgn = g_new (gfloat, result->width * result->height * NB_CPN);
+
+  gaussian_deriv (input_buf, dest_rgn, HORIZONTAL, std_dev, result);
+
+  find_max_gradient (dest_rgn, mdatas->h_grad, result->width, result->height);
+
+  gaussian_deriv (input_buf, dest_rgn, VERTICAL, std_dev, result);
+
+  find_max_gradient (dest_rgn, mdatas->v_grad, result->width, result->height);
+
+  /*  fill in the gradient map  */
+  gr = mdatas->m_grad;
+  dh = mdatas->h_grad;
+  dv = mdatas->v_grad;
+
+  for (i = 0; i < result->height; i++)
+    {
+      for (j = 0; j < result->width; j++, dh++, dv++, gr++)
+        {
+          /*  Find the gradient  */
+          if (!j || !i || (j == result->width - 1) || (i == result->height - 1))
+            {
+              *gr = MAG_THRESHOLD;
+            }
+          else
+            {
+              hmax = *dh - 0.5;
+              vmax = *dv - 0.5;
+
+              *gr = (gfloat) sqrt (SQR (hmax) + SQR (vmax));
+            }
+        }
+    }
+
+  g_free (dest_rgn);
+}
+
+static void
+find_max_gradient (gfloat *src_rgn,
+                   gfloat *dest_rgn,
+                   gint    width,
+                   gint    height)
+{
+  gfloat *s, *d;
+  gint    i, j, k;
+  gfloat  val;
+  gfloat  max;
+
+  s = src_rgn;
+  d = dest_rgn;
+
+  for (i = 0; i < height; i++)
+    {
+      for (j = 0; j < width; j++)
+        {
+          max = 0.5;
+          for (k = 0; k < NB_CPN; k++)
+            {
+              val = *s;
+              if (fabs (val - 0.5) > fabs (max - 0.5))
+                max = val;
+            }
+
+          *d++ = max;
+          s += NB_CPN;
+        }
+    }
+}
+
+static void
+gaussian_deriv (gfloat              *src_rgn,
+                gfloat              *dest_rgn,
+                Direction            direction,
+                gdouble              std_dev,
+                const GeglRectangle *result)
+{
+  gfloat *dp;
+  gfloat *sp, *s;
+  gfloat *data;
+  gfloat *buf, *b;
+  gint    chan;
+  gint    i, row, col;
+  gint    start, end;
+  gfloat  curve_array [9];
+  gfloat  sum_array [9];
+  gfloat *curve;
+  gfloat *sum;
+  gfloat  val;
+  gfloat  total;
+  gint    length;
+  gfloat  initial_p[4], initial_m[4];
+  gint    width = result->width, height = result->height;
+
+  /*  allocate buffers for rows/cols  */
+  length = MAX (result->width, result->height) * NB_CPN;
+  data = g_new (gfloat, length * 2);
+
+  length = 3;
+
+  /*  initialize  */
+  curve = curve_array + length;
+  sum = sum_array + length;
+  buf = g_new (gfloat, MAX (width, height) * NB_CPN);
+
+  if (direction == VERTICAL)
+    {
+      make_curve_d (curve, sum, std_dev, length);
+      total = sum[0] * -2;
+    }
+  else
+    {
+      make_curve (curve, sum, std_dev, length);
+      total = sum[length] + curve[length];
+    }
+
+  for (col = 0; col < width; col++)
+    {
+      sp = src_rgn + col * NB_CPN;
+      dp = dest_rgn + col * NB_CPN;;
+      b = buf;
+
+      for (chan = 0; chan < NB_CPN; chan++)
+        {
+          initial_p[chan] = sp[chan];
+          initial_m[chan] = sp[width * (height - 1) * NB_CPN + chan];
+        }
+
+      for (row = 0; row < height; row++)
+        {
+          start = (row < length) ?  - row : -length;
+          end = ((height - row - 1) < length) ? (height - row - 1) : length;
+
+          for (chan = 0; chan < NB_CPN; chan++)
+            {
+              s = sp + width * (start * NB_CPN) + chan;
+              val = 0;
+              i = start;
+
+              if (start != -length)
+                val += initial_p[chan] * (sum[start] - sum[-length]);
+
+              while (i <= end)
+                {
+                  val += *s * curve[i++];
+                  s += NB_CPN * width;
+                }
+
+              if (end != length)
+                val += initial_m[chan] * (sum[length] + curve[length] - sum[end+1]);
+
+              if ( val == 0)
+                *b++ = val;
+              else
+                *b++ = val / total;
+            }
+
+          sp += NB_CPN * width;
+        }
+
+      b = buf;
+      if (direction == VERTICAL)
+        for (row = 0; row < height; row++)
+          {
+            for (chan = 0; chan < NB_CPN; chan++)
+              {
+                b[chan] += 0.5;
+                dp[chan] = CLAMP01 (b[chan]);
+              }
+            b += NB_CPN;
+            dp += NB_CPN * width;
+          }
+      else
+        for (row = 0; row < height; row++)
+          {
+            for (chan = 0; chan < NB_CPN; chan++)
+              {
+                dp[chan] = CLAMP01 (b[chan]);
+              }
+            b += NB_CPN;
+            dp += NB_CPN * width;
+          }
+    }
+
+  if (direction == HORIZONTAL)
+    {
+      make_curve_d (curve, sum, std_dev, length);
+      total = sum[0] * -2;
+    }
+  else
+    {
+      make_curve (curve, sum, std_dev, length);
+      total = sum[length] + curve[length];
+    }
+
+  for (row = 0; row < height; row++)
+    {
+      sp = dest_rgn + width * row * NB_CPN;
+      dp = dest_rgn + width * row * NB_CPN;
+      b = buf;
+
+
+      for (chan = 0; chan < NB_CPN; chan++)
+        {
+          initial_p[chan] = sp[chan];
+          initial_m[chan] = sp[(width - 1) * NB_CPN + chan];
+        }
+
+      for (col = 0; col < width; col++)
+        {
+          start = (col < length) ?  - col : -length;
+          end = (width - col - 1 < length) ? (width - col - 1) : length;
+
+          for (chan = 0; chan < NB_CPN; chan++)
+            {
+              s = sp + (start * NB_CPN) + chan;
+              val = 0;
+              i = start;
+
+              if (start != -length)
+                val += initial_p[chan] * (sum[start] - sum[-length]);
+
+              while (i <= end)
+                {
+                  val += *s * curve[i++];
+                  s += NB_CPN;
+                }
+
+              if (end != length)
+                val += initial_m[chan] * (sum[length] + curve[length] - sum[end+1]);
+
+              if ( val == 0)
+                *b++ = val;
+              else
+                *b++ = val / total;
+            }
+
+          sp += NB_CPN;
+        }
+
+      b = buf;
+      if (direction == HORIZONTAL)
+        for (col = 0; col < width; col++)
+          {
+            for (chan = 0; chan < NB_CPN; chan++)
+              {
+                b[chan] += 0.5;
+                dp[chan] = CLAMP01 (b[chan]);
+              }
+            b += NB_CPN;
+            dp += NB_CPN;
+          }
+      else
+        for (col = 0; col < width; col++)
+          {
+            for (chan = 0; chan < NB_CPN; chan++)
+              {
+                dp[chan] = CLAMP01 (b[chan]);
+              }
+            b += NB_CPN;
+            dp += NB_CPN;
+          }
+    }
+
+  g_free (buf);
+  g_free (data);
+}
+
+/*
+ * The equations: g(r) = exp (- r^2 / (2 * sigma^2))
+ *                   r = sqrt (x^2 + y ^2)
+ */
+
+static void
+make_curve (gfloat  *curve,
+            gfloat  *sum,
+            gdouble  sigma,
+            gint     length)
+{
+  gdouble sigma2;
+  gint    i;
+
+  sigma2 = sigma * sigma;
+
+  curve[0] = 1.0;
+  for (i = 1; i <= length; i++)
+    {
+      curve[i] = (gfloat) (exp (- (i * i) / (2 * sigma2)));
+      curve[-i] = curve[i];
+    }
+
+  sum[-length] = 0;
+  for (i = -length+1; i <= length; i++)
+    sum[i] = sum[i-1] + curve[i-1];
+}
+
+
+/*
+ * The equations: d_g(r) = -r * exp (- r^2 / (2 * sigma^2)) / sigma^2
+ *                   r = sqrt (x^2 + y ^2)
+ */
+
+static void
+make_curve_d (gfloat  *curve,
+              gfloat  *sum,
+              gdouble  sigma,
+              gint     length)
+{
+  gdouble sigma2;
+  gint    i;
+
+  sigma2 = sigma * sigma;
+
+  curve[0] = 0;
+  for (i = 1; i <= length; i++)
+    {
+      curve[i] = (gfloat) ((i * exp (- (i * i) / (2 * sigma2)) / sigma2));
+      curve[-i] = -curve[i];
+    }
+
+  sum[-length] = 0;
+  sum[0] = 0;
+  for (i = 1; i <= length; i++)
+    {
+      sum[-length + i] = sum[-length + i - 1] + curve[-length + i - 1];
+      sum[i] = sum[i - 1] + curve[i - 1];
+    }
+}
+
+
+/*********************************************/
+/*   Functions for grid manipulation         */
+/*********************************************/
+
+static void
+grid_create_squares (const GeglRectangle *result,
+                     gdouble              tile_size,
+                     GridDescriptor      *grid)
+{
+  gint    rows, cols;
+  gint    width, height;
+  gdouble pre_cols, pre_rows;
+  gdouble offset_x, offset_y;
+  gint    i, j;
+  gint    size = (gint) tile_size;
+  Vertex *pt;
+
+  pre_cols = (result->x / size) * size;
+  pre_rows = (result->y / size) * size;
+
+  offset_x = pre_cols - result->x;
+  offset_y = pre_rows - result->y;
+
+  width = result->width - offset_x;
+  height = result->height - offset_y;
+
+  rows = (height + size - 1) / size;
+  cols = (width + size - 1) / size;
+
+  grid->vert_o = grid->vert = g_new (Vertex, (cols + 2) * (rows + 2));
+  grid->vert += (cols + 2) + 1;
+
+  for (i = -1; i <= rows; i++)
+    for (j = -1; j <= cols; j++)
+      {
+        pt = grid->vert + (i * (cols + 2) + j);
+
+        pt->x = j * size + size / 2 + offset_x;
+        pt->y = i * size + size / 2 + offset_y;
+      }
+
+  grid->vert_rows = rows;
+  grid->vert_cols = cols;
+  grid->row_pad = 1;
+  grid->col_pad = 1;
+  grid->vert_multiple = 1;
+  grid->vert_rowstride = cols + 2;
+}
+
+static void
+grid_create_hexagons (const GeglRectangle *result,
+                      gdouble              tile_size,
+                      GridDescriptor      *grid)
+{
+  gint     rows, cols;
+  gint     i, j;
+  gint     width, height;
+  gdouble  pre_rows, pre_cols;
+  gdouble  hex_l1, hex_l2, hex_l3;
+  gdouble  hex_width;
+  gdouble  hex_height;
+  gdouble  offset_x, offset_y;
+  Vertex  *pt;
+  hex_l1 = tile_size / 2.0;
+  hex_l2 = hex_l1 * 2.0 / sqrt (3.0);
+  hex_l3 = hex_l1 / sqrt (3.0);
+  hex_width = 6 * hex_l1 / sqrt (3.0);
+  hex_height = tile_size;
+
+  pre_cols = floor (result->x / hex_width) * hex_width;
+  pre_rows = floor (result->y / hex_height) * hex_height;
+
+  offset_x = pre_cols - result->x;
+  offset_y = pre_rows - result->y;
+
+  width = result->width - offset_x;
+  height = result->height - offset_y;
+
+  rows = ((height + hex_height - 1) / hex_height);
+  cols = ((width + hex_width * 2 - 1) / hex_width);
+
+  grid->vert_o = grid->vert = g_new (Vertex, (cols + 2) * 4 * (rows + 2));
+  grid->vert += (cols + 2) * 4 + 4;
+
+  for (i = -1; i <= rows; i++)
+    for (j = -1; j <= cols; j++)
+      {
+        pt = grid->vert + (i * (cols + 2) * 4 + j * 4);
+
+        pt[0].x = hex_width * j + hex_l3 + offset_x;
+        pt[0].y = hex_height * i + offset_y;
+        pt[1].x = pt[0].x + hex_l2;
+        pt[1].y = pt[0].y;
+        pt[2].x = pt[1].x + hex_l3;
+        pt[2].y = pt[1].y + hex_l1;
+        pt[3].x = pt[0].x - hex_l3;
+        pt[3].y = pt[0].y + hex_l1;
+      }
+
+  grid->vert_rows = rows;
+  grid->vert_cols = cols;
+  grid->row_pad = 1;
+  grid->col_pad = 1;
+  grid->vert_multiple = 4;
+  grid->vert_rowstride = (cols + 2) * 4;
+}
+
+
+static void
+grid_create_octagons (const GeglRectangle *result,
+                      gdouble              tile_size,
+                      GridDescriptor      *grid)
+{
+  gint     rows, cols;
+  gint     width, height;
+  gdouble  pre_rows, pre_cols;
+  gint     i, j;
+  gdouble  ts, side, leg;
+  gdouble  oct_size;
+  Vertex  *pt;
+  gdouble  offset_x, offset_y;
+
+  ts = tile_size;
+  side = ts / (sqrt (2.0) + 1.0);
+  leg = side * sqrt (2.0) * 0.5;
+  oct_size = ts + side;
+
+  pre_cols = floor (result->x / oct_size) * oct_size;
+  pre_rows = floor (result->y / oct_size) * oct_size;
+
+  offset_x = pre_cols - result->x;
+  offset_y = pre_rows - result->y;
+
+  width = result->width - offset_x;
+  height = result->height - offset_y;
+
+  rows = ((height + oct_size - 1) / oct_size);
+  cols = ((width + oct_size * 2 - 1) / oct_size);
+
+  grid->vert_o = grid->vert = g_new (Vertex, (cols + 2) * 8 * (rows + 2));
+  grid->vert += (cols + 2) * 8 + 8;
+
+  for (i = -1; i < rows + 1; i++)
+    for (j = -1; j < cols + 1; j++)
+      {
+        pt = grid->vert + (i * (cols + 2) * 8 + j * 8);
+
+        pt[0].x = oct_size * j + offset_x;
+        pt[0].y = oct_size * i + offset_y;
+        pt[1].x = pt[0].x + side;
+        pt[1].y = pt[0].y;
+        pt[2].x = pt[0].x + leg + side;
+        pt[2].y = pt[0].y + leg;
+        pt[3].x = pt[2].x;
+        pt[3].y = pt[0].y + leg + side;
+        pt[4].x = pt[1].x;
+        pt[4].y = pt[0].y + 2 * leg + side;
+        pt[5].x = pt[0].x;
+        pt[5].y = pt[4].y;
+        pt[6].x = pt[0].x - leg;
+        pt[6].y = pt[3].y;
+        pt[7].x = pt[6].x;
+        pt[7].y = pt[2].y;
+      }
+
+  grid->vert_rows = rows;
+  grid->vert_cols = cols;
+  grid->row_pad = 1;
+  grid->col_pad = 1;
+  grid->vert_multiple = 8;
+  grid->vert_rowstride = (cols + 2) * 8;
+}
+
+
+static void
+grid_create_triangles (const GeglRectangle *result,
+                       gdouble              tile_size,
+                       GridDescriptor      *grid)
+{
+  gint     rows, cols;
+  gdouble  pre_rows, pre_cols;
+  gint     width, height;
+  gint     i, j;
+  gdouble  tri_mid, tri_height;
+  Vertex  *pt;
+  gdouble  offset_x, offset_y;
+
+  tri_mid    = tile_size / 2.0;              /* cos 60 */
+  tri_height = tile_size / 2.0 * sqrt (3.0); /* sin 60 */
+
+  pre_cols = floor (result->x / tile_size) * tile_size;
+  pre_rows = floor (result->y / (2 * tri_height)) * (2 * tri_height);
+
+  offset_x = pre_cols - result->x;
+  offset_y = pre_rows - result->y;
+
+  width = result->width - offset_x;
+  height = result->height - offset_y;
+
+  rows = (height + 2 * tri_height - 1) / (2 * tri_height);
+  cols = (width + tile_size - 1) / tile_size;
+
+  grid->vert_o = grid->vert = g_new (Vertex, (cols + 2) * 2 * (rows + 2));
+  grid->vert += (cols + 2) * 2 + 2;
+
+  for (i = -1; i <= rows; i++)
+    for (j = -1; j <= cols; j++)
+      {
+        pt = grid->vert + (i * (cols + 2) * 2 + j * 2);
+
+        pt[0].x = tile_size * j + offset_x;
+        pt[0].y = (tri_height * 2) * i + offset_y;
+        pt[1].x = pt[0].x + tri_mid;
+        pt[1].y = pt[0].y + tri_height;
+      }
+
+  grid->vert_rows = rows;
+  grid->vert_cols = cols;
+  grid->row_pad = 1;
+  grid->col_pad = 1;
+  grid->vert_multiple = 2;
+  grid->vert_rowstride = (cols + 2) * 2;
+}
+
+static void
+grid_localize (const GeglRectangle *result,
+               GeglChantO          *o,
+               MosaicDatas         *mdatas)
+{
+  gint           i, j;
+  gint           k, l;
+  gint           x3, y3, x4, y4;
+  gint           size;
+  gint           max_x, max_y;
+  gfloat         max;
+  gfloat        *data;
+  gdouble        rand_localize;
+  Vertex        *pt;
+  GridDescriptor grid;
+
+  grid = mdatas->grid;
+
+  size = (gint) o->tile_size;
+  rand_localize = size * (1.0 - o->tile_neatness);
+
+  for (i = -grid.row_pad; i < grid.vert_rows + grid.row_pad; i++)
+    for (j = -grid.col_pad * grid.vert_multiple;
+         j < (grid.vert_cols + grid.col_pad) * grid.vert_multiple;
+         j++)
+      {
+        pt = grid.vert + (i * grid.vert_rowstride + j);
+
+        if (result->x + pt->x < 0 || result->x + pt->x >= mdatas->width ||
+            result->y + pt->y < 0 || result->y + pt->y >= mdatas->height)
+          continue;
+
+        if (rand_localize > EPSILON)
+          {
+            /* Rely on global values to make gegl's tiles seamless */
+            max_x = pt->x + (gint) rand_f (o->seed,
+                                           result->x + pt->x, result->y + pt->y,
+                                           - rand_localize / 2.0, rand_localize / 2.0);
+            max_y = pt->y + (gint) rand_f (o->seed,
+                                           result->y + pt->y, result->x + pt->x, /*sic*/
+                                           - rand_localize / 2.0, rand_localize / 2.0);
+          }
+        else
+          {
+            max_x = pt->x;
+            max_y = pt->y;
+          }
+
+        x3 = pt->x - (gint) (rand_localize / 2.0);
+        y3 = pt->y - (gint) (rand_localize / 2.0);
+        x4 = x3 + (gint) rand_localize;
+        y4 = y3 + (gint) rand_localize;
+
+        x3 = CLAMP (x3, 0, result->width  - 1);
+        y3 = CLAMP (y3, 0, result->height - 1);
+        x4 = CLAMP (x4, 0, result->width  - 1);
+        y4 = CLAMP (y4, 0, result->height - 1);
+
+        max = * (mdatas->m_grad + y3 * result->width + x3);
+        data = mdatas->m_grad + result->width * y3;
+
+        for (k = y3; k <= y4; k++)
+          {
+            for (l = x3; l <= x4; l++)
+              {
+                if (data[l] > max)
+                  {
+                    max_y = k;
+                    max_x = l;
+                    max = data[l];
+                  }
+              }
+            data += result->width;
+          }
+
+        pt->x = max_x;
+        pt->y = max_y;
+      }
+}
+
+static gfloat*
+grid_render (gfloat              *input_buf,
+             const GeglRectangle *result,
+             GeglChantO          *o,
+             MosaicDatas         *mdatas)
+{
+  gint            i, j, k, index;
+  gfloat          col[4];
+  Polygon         poly;
+  gfloat         *output_buf, *o_buf;
+  GridDescriptor  grid;
+
+  grid = mdatas->grid;
+
+  output_buf = g_new (gfloat, NB_CPN * (result->width) * (result->height));
+
+  o_buf = output_buf;
+
+  for (i = 0; i < result->height; i++)
+    {
+      for (j = 0; j < result->width; j++)
+        {
+          for (k = 0; k < NB_CPN; k++)
+            o_buf[k] = mdatas->back[k];
+          o_buf += NB_CPN;
+        }
+    }
+
+  for (i = -grid.row_pad; i < grid.vert_rows; i++)
+    for (j = -grid.col_pad; j < grid.vert_cols; j++)
+      {
+        index = i * grid.vert_rowstride + j * grid.vert_multiple;
+
+        switch (o->tile_type)
+          {
+          case SQUARES:
+            polygon_reset (&poly);
+            polygon_add_point (&poly,
+                               grid.vert[index].x,
+                               grid.vert[index].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + 1].x,
+                               grid.vert[index + 1].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + grid.vert_rowstride + 1].x,
+                               grid.vert[index + grid.vert_rowstride + 1].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + grid.vert_rowstride].x,
+                               grid.vert[index + grid.vert_rowstride].y);
+
+            process_poly (&poly, o->tile_allow_split, input_buf, output_buf, col,
+                          result, o, mdatas);
+            break;
+
+          case HEXAGONS:
+            /*  The main hexagon  */
+            polygon_reset (&poly);
+            polygon_add_point (&poly,
+                               grid.vert[index].x,
+                               grid.vert[index].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + 1].x,
+                               grid.vert[index + 1].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + 2].x,
+                               grid.vert[index + 2].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + grid.vert_rowstride + 1].x,
+                               grid.vert[index + grid.vert_rowstride + 1].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + grid.vert_rowstride].x,
+                               grid.vert[index + grid.vert_rowstride].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + 3].x,
+                               grid.vert[index + 3].y);
+            process_poly (&poly, o->tile_allow_split, input_buf, output_buf, col,
+                          result, o, mdatas);
+
+            /*  The auxiliary hexagon  */
+            polygon_reset (&poly);
+            polygon_add_point (&poly,
+                               grid.vert[index + 2].x,
+                               grid.vert[index + 2].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + grid.vert_multiple * 2 - 1].x,
+                               grid.vert[index + grid.vert_multiple * 2 - 1].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + grid.vert_rowstride + grid.vert_multiple].x,
+                               grid.vert[index + grid.vert_rowstride + grid.vert_multiple].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + grid.vert_rowstride + grid.vert_multiple + 3].x,
+                               grid.vert[index + grid.vert_rowstride + grid.vert_multiple + 3].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + grid.vert_rowstride + 2].x,
+                               grid.vert[index + grid.vert_rowstride + 2].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + grid.vert_rowstride + 1].x,
+                               grid.vert[index + grid.vert_rowstride + 1].y);
+            process_poly (&poly, o->tile_allow_split, input_buf, output_buf, col,
+                          result, o, mdatas);
+            break;
+
+          case OCTAGONS:
+            /*  The main octagon  */
+            polygon_reset (&poly);
+            for (k = 0; k < 8; k++)
+              polygon_add_point (&poly,
+                                 grid.vert[index + k].x,
+                                 grid.vert[index + k].y);
+            process_poly (&poly, o->tile_allow_split, input_buf, output_buf, col,
+                          result, o, mdatas);
+
+            /*  The auxiliary octagon  */
+            polygon_reset (&poly);
+            polygon_add_point (&poly,
+                               grid.vert[index + 3].x,
+                               grid.vert[index + 3].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + grid.vert_multiple * 2 - 2].x,
+                               grid.vert[index + grid.vert_multiple * 2 - 2].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + grid.vert_multiple * 2 - 3].x,
+                               grid.vert[index + grid.vert_multiple * 2 - 3].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + grid.vert_rowstride + grid.vert_multiple].x,
+                               grid.vert[index + grid.vert_rowstride + grid.vert_multiple].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + grid.vert_rowstride + grid.vert_multiple * 2 - 1].x,
+                               grid.vert[index + grid.vert_rowstride + grid.vert_multiple * 2 - 1].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + grid.vert_rowstride + 2].x,
+                               grid.vert[index + grid.vert_rowstride + 2].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + grid.vert_rowstride + 1].x,
+                               grid.vert[index + grid.vert_rowstride + 1].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + 4].x,
+                               grid.vert[index + 4].y);
+            process_poly (&poly, o->tile_allow_split, input_buf, output_buf, col,
+                          result, o, mdatas);
+
+            /*  The main square  */
+            polygon_reset (&poly);
+            polygon_add_point (&poly,
+                               grid.vert[index + 2].x,
+                               grid.vert[index + 2].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + grid.vert_multiple * 2 - 1].x,
+                               grid.vert[index + grid.vert_multiple * 2 - 1].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + grid.vert_multiple * 2 - 2].x,
+                               grid.vert[index + grid.vert_multiple * 2 - 2].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + 3].x,
+                               grid.vert[index + 3].y);
+            process_poly (&poly, FALSE, input_buf, output_buf, col,
+                          result, o, mdatas);
+
+            /*  The auxiliary square  */
+            polygon_reset (&poly);
+            polygon_add_point (&poly,
+                               grid.vert[index + 5].x,
+                               grid.vert[index + 5].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + 4].x,
+                               grid.vert[index + 4].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + grid.vert_rowstride + 1].x,
+                               grid.vert[index + grid.vert_rowstride + 1].y);
+            polygon_add_point (&poly,
+                               grid.vert[index + grid.vert_rowstride].x,
+                               grid.vert[index + grid.vert_rowstride].y);
+            process_poly (&poly, FALSE, input_buf, output_buf, col,
+                          result, o, mdatas);
+            break;
+          case TRIANGLES:
+            /*  Lower left  */
+            polygon_reset (&poly);
+            polygon_add_point (&poly,
+                                grid.vert[index].x,
+                                grid.vert[index].y);
+            polygon_add_point (&poly,
+                                grid.vert[index + grid.vert_multiple].x,
+                                grid.vert[index + grid.vert_multiple].y);
+            polygon_add_point (&poly,
+                                grid.vert[index + 1].x,
+                                grid.vert[index + 1].y);
+            process_poly (&poly, o->tile_allow_split, input_buf, output_buf, col,
+                          result, o, mdatas);
+
+            /*  lower right  */
+            polygon_reset (&poly);
+            polygon_add_point (&poly,
+                                grid.vert[index + 1].x,
+                                grid.vert[index + 1].y);
+            polygon_add_point (&poly,
+                                grid.vert[index + grid.vert_multiple].x,
+                                grid.vert[index + grid.vert_multiple].y);
+            polygon_add_point (&poly,
+                                grid.vert[index + grid.vert_multiple + 1].x,
+                                grid.vert[index + grid.vert_multiple + 1].y);
+            process_poly (&poly, o->tile_allow_split, input_buf, output_buf, col,
+                          result, o, mdatas);
+
+            /*  upper left  */
+            polygon_reset (&poly);
+            polygon_add_point (&poly,
+                                grid.vert[index + 1].x,
+                                grid.vert[index + 1].y);
+            polygon_add_point (&poly,
+                                grid.vert[index + grid.vert_multiple + grid.vert_rowstride].x,
+                                grid.vert[index + grid.vert_multiple + grid.vert_rowstride].y);
+            polygon_add_point (&poly,
+                                grid.vert[index + grid.vert_rowstride].x,
+                                grid.vert[index + grid.vert_rowstride].y);
+            process_poly (&poly, o->tile_allow_split, input_buf, output_buf, col,
+                          result, o, mdatas);
+
+            /*  upper right  */
+            polygon_reset (&poly);
+            polygon_add_point (&poly,
+                                grid.vert[index + 1].x,
+                                grid.vert[index + 1].y);
+            polygon_add_point (&poly,
+                                grid.vert[index + grid.vert_multiple +1 ].x,
+                                grid.vert[index + grid.vert_multiple +1 ].y);
+            polygon_add_point (&poly,
+                                grid.vert[index + grid.vert_multiple + grid.vert_rowstride].x,
+                                grid.vert[index + grid.vert_multiple + grid.vert_rowstride].y);
+            process_poly (&poly, o->tile_allow_split, input_buf, output_buf, col,
+                          result, o, mdatas);
+            break;
+
+          }
+      }
+  return output_buf;
+}
+
+static void
+process_poly (Polygon             *poly,
+              gboolean             allow_split,
+              gfloat              *input_buf,
+              gfloat              *output_buf,
+              gfloat              *col,
+              const GeglRectangle *result,
+              GeglChantO          *o,
+              MosaicDatas         *mdatas)
+{
+  gdouble  dir[2];
+  gdouble  loc[2];
+  gdouble  cx = 0.0, cy = 0.0;
+  gdouble  magnitude;
+  gdouble  distance;
+  gboolean vary;
+  gint     size, frac_size;
+  gdouble  color_vary = 0;
+
+  /*  Determine direction of edges inside polygon, if any  */
+  find_poly_dir (poly, mdatas->m_grad, mdatas->h_grad, mdatas->v_grad,
+                 dir, loc, result);
+
+  magnitude = sqrt (SQR (dir[0] - 0.5) + SQR (dir[1] - 0.5));
+
+  /*  Find the center of the polygon  */
+  polygon_find_center (poly, &cx, &cy);
+  distance = sqrt (SQR (loc[0] - cx) + SQR (loc[1] - cy));
+
+  /* Here we rely on global values and coordinates to get predictable results,
+   * and to make gegl's tiles seamless */
+  size = (mdatas->width * mdatas->height);
+  frac_size = size * o->color_variation;
+
+  vary = rand_i (o->seed, result->x + cx, result->y + cy, 0, size) < frac_size;
+
+  /*  determine the variation of tile color based on tile number  */
+  if (vary)
+    {
+      color_vary = rand_f (o->seed, result->x + cx, result->y + cy,
+                           - o->color_variation * 0.5, o->color_variation * 0.5);
+    }
+
+  /*  If the magnitude of direction inside the polygon is greater than
+   *  THRESHOLD, split the polygon into two new polygons
+   */
+  if (magnitude > MAG_THRESHOLD &&
+      (2 * distance / o->tile_size) < 0.5 && allow_split)
+    {
+      split_poly (poly, input_buf, output_buf,
+                  col, dir, color_vary, result, o, mdatas);
+    }
+  else
+    {
+      /*  Otherwise, render the original polygon  */
+      render_poly (poly, input_buf, output_buf,
+                   col, color_vary, result, o, mdatas);
+    }
+}
+
+static void
+render_poly (Polygon             *poly,
+             gfloat              *input_buf,
+             gfloat              *output_buf,
+             gfloat              *col,
+             gdouble              vary,
+             const GeglRectangle *result,
+             GeglChantO          *o,
+             MosaicDatas         *mdatas)
+{
+  gdouble cx = 0.0;
+  gdouble cy = 0.0;
+
+  polygon_find_center (poly, &cx, &cy);
+
+  if (o->color_averaging)
+    find_poly_color (poly, input_buf, col, vary, result);
+
+  scale_poly (poly, cx, cy, mdatas->scale);
+
+  if (o->color_averaging)
+    fill_poly_color (poly, input_buf, output_buf, col, result,
+                     o->antialiasing, o->tile_surface, o->tile_height, mdatas);
+  else
+    fill_poly_image (poly, input_buf, output_buf, vary, result,
+                     o->antialiasing, o->tile_surface, o->tile_height, mdatas);
+}
+
+static void
+split_poly (Polygon             *poly,
+            gfloat              *input_buf,
+            gfloat              *output_buf,
+            gfloat              *col,
+            gdouble             *dir,
+            gdouble              vary,
+            const GeglRectangle *result,
+            GeglChantO          *o,
+            MosaicDatas         *mdatas)
+{
+  Polygon new_poly;
+  gdouble spacing;
+  gdouble cx = 0.0;
+  gdouble cy = 0.0;
+  gdouble magnitude;
+  gdouble vec[2];
+  gdouble pt[2];
+
+  spacing = o->tile_spacing / (2.0 * mdatas->scale);
+
+  polygon_find_center (poly, &cx, &cy);
+  polygon_translate (poly, -cx, -cy);
+
+  magnitude = sqrt (SQR (dir[0] - 0.5) + SQR (dir[1] - 0.5));
+  vec[0] = - (dir[1] - 0.5) / magnitude;
+  vec[1] =   (dir[0] - 0.5) / magnitude;
+  pt[0] = -vec[1] * spacing;
+  pt[1] =  vec[0] * spacing;
+
+  polygon_reset (&new_poly);
+  clip_poly (vec, pt, poly, &new_poly);
+  polygon_translate (&new_poly, cx, cy);
+
+  if (new_poly.npts)
+    {
+      if (o->color_averaging)
+        find_poly_color (&new_poly, input_buf, col, vary, result);
+
+      scale_poly (&new_poly, cx, cy, mdatas->scale);
+
+      if (o->color_averaging)
+        fill_poly_color (&new_poly, input_buf, output_buf, col, result,
+                         o->antialiasing, o->tile_surface, o->tile_height,
+                         mdatas);
+      else
+        fill_poly_image (&new_poly, input_buf, output_buf, vary, result,
+                         o->antialiasing, o->tile_surface, o->tile_height,
+                         mdatas);
+    }
+
+  vec[0] = -vec[0];
+  vec[1] = -vec[1];
+  pt[0] = -pt[0];
+  pt[1] = -pt[1];
+
+  polygon_reset (&new_poly);
+  clip_poly (vec, pt, poly, &new_poly);
+  polygon_translate (&new_poly, cx, cy);
+
+  if (new_poly.npts)
+    {
+      if (o->color_averaging)
+        find_poly_color (&new_poly, input_buf, col, vary, result);
+
+      scale_poly (&new_poly, cx, cy, mdatas->scale);
+
+      if (o->color_averaging)
+        fill_poly_color (&new_poly, input_buf, output_buf, col, result,
+                         o->antialiasing, o->tile_surface, o->tile_height,
+                         mdatas);
+      else
+        fill_poly_image (&new_poly, input_buf, output_buf, vary, result,
+                         o->antialiasing, o->tile_surface, o->tile_height,
+                         mdatas);
+    }
+}
+
+static void
+clip_poly (gdouble *dir,
+           gdouble *pt,
+           Polygon *poly,
+           Polygon *poly_new)
+{
+  gint    i;
+  gdouble x1, y1, x2, y2;
+
+  for (i = 0; i < poly->npts; i++)
+    {
+      x1 = (i) ? poly->pts[i-1].x : poly->pts[poly->npts-1].x;
+      y1 = (i) ? poly->pts[i-1].y : poly->pts[poly->npts-1].y;
+      x2 = poly->pts[i].x;
+      y2 = poly->pts[i].y;
+
+      clip_point (dir, pt, x1, y1, x2, y2, poly_new);
+    }
+}
+
+
+static void
+clip_point (gdouble *dir,
+            gdouble *pt,
+            gdouble  x1,
+            gdouble  y1,
+            gdouble  x2,
+            gdouble  y2,
+            Polygon *poly_new)
+{
+  gdouble det, m11, m12;
+  gdouble side1, side2;
+  gdouble t;
+  gdouble vec[2];
+
+  x1 -= pt[0]; x2 -= pt[0];
+  y1 -= pt[1]; y2 -= pt[1];
+
+  side1 = x1 * -dir[1] + y1 * dir[0];
+  side2 = x2 * -dir[1] + y2 * dir[0];
+
+  /*  If both points are to be clipped, ignore  */
+  if (side1 < 0.0 && side2 < 0.0)
+    {
+      return;
+    }
+  /*  If both points are non-clipped, set point  */
+  else if (side1 >= 0.0 && side2 >= 0.0)
+    {
+      polygon_add_point (poly_new, x2 + pt[0], y2 + pt[1]);
+      return;
+    }
+  /*  Otherwise, there is an intersection...  */
+  else
+    {
+      vec[0] = x1 - x2;
+      vec[1] = y1 - y2;
+      det = dir[0] * vec[1] - dir[1] * vec[0];
+
+      if (det == 0.0)
+        {
+          polygon_add_point (poly_new, x2 + pt[0], y2 + pt[1]);
+          return;
+        }
+
+      m11 = vec[1] / det;
+      m12 = -vec[0] / det;
+
+      t = m11 * x1 + m12 * y1;
+
+      /*  If the first point is clipped, set intersection and point  */
+      if (side1 < 0.0 && side2 > 0.0)
+        {
+          polygon_add_point (poly_new, dir[0] * t + pt[0], dir[1] * t + pt[1]);
+          polygon_add_point (poly_new, x2 + pt[0], y2 + pt[1]);
+        }
+      else
+        {
+          polygon_add_point (poly_new, dir[0] * t + pt[0], dir[1] * t + pt[1]);
+        }
+    }
+}
+
+static void
+find_poly_dir (Polygon             *poly,
+               gfloat              *m_gr,
+               gfloat              *h_gr,
+               gfloat              *v_gr,
+               gdouble             *dir,
+               gdouble             *loc,
+               const GeglRectangle *result)
+{
+  gdouble dmin_x = 0.0, dmin_y = 0.0;
+  gdouble dmax_x = 0.0, dmax_y = 0.0;
+  gint    xs, ys;
+  gint    xe, ye;
+  gint    min_x, min_y;
+  gint    max_x, max_y;
+  gint    size_y;
+  gint   *max_scanlines;
+  gint   *min_scanlines;
+  gfloat *dm, *dv, *dh;
+  gint    count, total;
+  gint    rowstride;
+  gint    i, j;
+
+  rowstride = result->width;
+  count = 0;
+  total = 0;
+  dir[0] = 0.0;
+  dir[1] = 0.0;
+  loc[0] = 0.0;
+  loc[1] = 0.0;
+
+  polygon_extents (poly, &dmin_x, &dmin_y, &dmax_x, &dmax_y);
+
+  min_x = (gint) dmin_x;
+  min_y = (gint) dmin_y;
+  max_x = (gint) dmax_x;
+  max_y = (gint) dmax_y;
+
+  size_y = max_y - min_y;
+
+  min_scanlines = g_new (gint, size_y);
+  max_scanlines = g_new (gint, size_y);
+
+  for (i = 0; i < size_y; i++)
+    {
+      min_scanlines[i] = max_x;
+      max_scanlines[i] = min_x;
+    }
+
+  for (i = 0; i < poly->npts; i++)
+    {
+      xs = (gint) ((i) ? poly->pts[i-1].x : poly->pts[poly->npts-1].x);
+      ys = (gint) ((i) ? poly->pts[i-1].y : poly->pts[poly->npts-1].y);
+      xe = (gint) poly->pts[i].x;
+      ye = (gint) poly->pts[i].y;
+
+      convert_segment (xs, ys, xe, ye, min_y,
+                       min_scanlines, max_scanlines);
+    }
+
+  for (i = 0; i < size_y; i++)
+    {
+      if ((i + min_y) >= 0 && (i + min_y) < result->height)
+        {
+          dm = m_gr + (i + min_y) * rowstride;
+          dh = h_gr + (i + min_y) * rowstride;
+          dv = v_gr + (i + min_y) * rowstride;
+
+          for (j = min_scanlines[i]; j < max_scanlines[i]; j++)
+            {
+              if (j >= 0 && j < result->width)
+                {
+                  if (dm[j] > MAG_THRESHOLD)
+                    {
+                      dir[0] += dh[j];
+                      dir[1] += dv[j];
+                      loc[0] += j;
+                      loc[1] += i + min_y;
+                      count++;
+                    }
+                  total++;
+                }
+            }
+        }
+    }
+
+  if (!total)
+    {
+      g_free (max_scanlines);
+      g_free (min_scanlines);
+      return;
+    }
+
+  if ((gdouble) count / (gdouble) total > COUNT_THRESHOLD)
+    {
+      dir[0] /= count;
+      dir[1] /= count;
+      loc[0] /= count;
+      loc[1] /= count;
+    }
+  else
+    {
+      dir[0] = 0.5;
+      dir[1] = 0.5;
+      loc[0] = 0.0;
+      loc[1] = 0.0;
+    }
+
+  g_free (min_scanlines);
+  g_free (max_scanlines);
+}
+
+static void
+find_poly_color (Polygon             *poly,
+                 gfloat              *input_buf,
+                 gfloat              *col,
+                 gdouble              color_var,
+                 const GeglRectangle *result)
+{
+  gdouble       dmin_x = 0.0, dmin_y = 0.0;
+  gdouble       dmax_x = 0.0, dmax_y = 0.0;
+  gint          xs, ys, xe, ye;
+  gint          min_x, min_y, max_x, max_y;
+  gint          size_y;
+  gint         *max_scanlines, *min_scanlines;
+  gfloat        col_sum[4] = {0, 0, 0, 0};
+  gint          b, count;
+  gint          i, j, y;
+
+  count = 0;
+
+  polygon_extents (poly, &dmin_x, &dmin_y, &dmax_x, &dmax_y);
+
+  min_x = (gint) dmin_x;
+  min_y = (gint) dmin_y;
+  max_x = (gint) dmax_x;
+  max_y = (gint) dmax_y;
+
+  size_y = max_y - min_y;
+
+  min_scanlines = g_new (gint, size_y);
+  max_scanlines = g_new (gint, size_y);
+
+  for (i = 0; i < size_y; i++)
+    {
+      min_scanlines[i] = max_x;
+      max_scanlines[i] = min_x;
+    }
+
+  for (i = 0; i < poly->npts; i++)
+    {
+      xs = (gint) ((i) ? poly->pts[i-1].x : poly->pts[poly->npts-1].x);
+      ys = (gint) ((i) ? poly->pts[i-1].y : poly->pts[poly->npts-1].y);
+      xe = (gint) poly->pts[i].x;
+      ye = (gint) poly->pts[i].y;
+
+      convert_segment (xs, ys, xe, ye, min_y,
+                       min_scanlines, max_scanlines);
+    }
+
+  for (i = 0; i < size_y; i++)
+    {
+      y = i + min_y;
+
+      if (y >= 0 && y < result->height)
+        {
+          for (j = min_scanlines[i]; j < max_scanlines[i]; j++)
+            {
+              if (j >= 0 && j < result->width)
+                {
+                  gint id = (j + y * result->width) * NB_CPN;
+
+                  for (b = 0; b < NB_CPN; b++)
+                    col_sum[b] += input_buf[id + b];
+
+                  count++;
+                }
+            }
+        }
+    }
+
+  if (count)
+    {
+      /* but alpha */
+      for (b = 0; b < NB_CPN-1; b++)
+        {
+          col_sum[b] = (col_sum[b] / count + color_var);
+          col[b] = CLAMP01 (col_sum[b]);
+        }
+      col_sum[NB_CPN -1] = (col_sum[NB_CPN - 1] / count);
+    }
+
+  g_free (min_scanlines);
+  g_free (max_scanlines);
+}
+
+static void
+scale_poly (Polygon *poly,
+            gdouble  tx,
+            gdouble  ty,
+            gdouble  poly_scale)
+{
+  polygon_translate (poly, -tx, -ty);
+  polygon_scale (poly, poly_scale);
+  polygon_translate (poly, tx, ty);
+}
+
+static void
+fill_poly_color (Polygon             *poly,
+                 gfloat              *input_buf,
+                 gfloat              *output_buf,
+                 gfloat              *col,
+                 const GeglRectangle *result,
+                 gboolean             antialiasing,
+                 gboolean             tile_rough,
+                 gdouble              tile_height,
+                 MosaicDatas         *mdatas)
+{
+  gdouble    dmin_x = 0.0, dmin_y = 0.0;
+  gdouble    dmax_x = 0.0, dmax_y = 0.0;
+  gint       xs, ys, xe, ye;
+  gint       min_x, min_y, max_x, max_y;
+  gint       size_x, size_y;
+  gint      *max_scanlines, *max_scanlines_iter;
+  gint      *min_scanlines, *min_scanlines_iter;
+  gfloat    *vals;
+  gfloat     val, pixel;
+  gfloat     buf[NB_CPN];
+  gint       b, i, j, k, x, y;
+  gdouble    contrib;
+  gdouble    xx, yy;
+  gint       supersample, supersample2;
+  Vertex    *pts_tmp;
+  const gint poly_npts = poly->npts;
+
+  /*  Determine antialiasing  */
+  if (antialiasing)
+    {
+      supersample = SUPERSAMPLE;
+      supersample2 = SQR (supersample);
+    }
+  else
+    {
+      supersample = supersample2 = 1;
+    }
+
+  if (poly_npts)
+    {
+      pts_tmp = poly->pts;
+      xs = (gint) pts_tmp[poly_npts - 1].x;
+      ys = (gint) pts_tmp[poly_npts - 1].y;
+      xe = (gint) pts_tmp->x;
+      ye = (gint) pts_tmp->y;
+
+      calc_spec_vec (mdatas->vecs, xs, ys, xe, ye,
+                     mdatas->light_x, mdatas->light_y);
+
+      for (i = 1; i < poly_npts; i++)
+        {
+          xs = (gint) (pts_tmp->x);
+          ys = (gint) (pts_tmp->y);
+          pts_tmp++;
+          xe = (gint) pts_tmp->x;
+          ye = (gint) pts_tmp->y;
+
+          calc_spec_vec (mdatas->vecs+i, xs, ys, xe, ye,
+                         mdatas->light_x, mdatas->light_y);
+        }
+    }
+
+  polygon_extents (poly, &dmin_x, &dmin_y, &dmax_x, &dmax_y);
+
+  min_x = (gint) dmin_x;
+  min_y = (gint) dmin_y;
+  max_x = (gint) dmax_x;
+  max_y = (gint) dmax_y;
+
+  size_y = (max_y - min_y) * supersample;
+  size_x = (max_x - min_x) * supersample;
+
+  min_scanlines = min_scanlines_iter = g_new (gint, size_y);
+  max_scanlines = max_scanlines_iter = g_new (gint, size_y);
+
+  for (i = 0; i < size_y; i++)
+    {
+      min_scanlines[i] = max_x * supersample;
+      max_scanlines[i] = min_x * supersample;
+    }
+
+  if (poly_npts)
+    {
+      pts_tmp = poly->pts;
+      xs = (gint) pts_tmp[poly_npts-1].x;
+      ys = (gint) pts_tmp[poly_npts-1].y;
+      xe = (gint) pts_tmp->x;
+      ye = (gint) pts_tmp->y;
+
+      xs *= supersample;
+      ys *= supersample;
+      xe *= supersample;
+      ye *= supersample;
+
+      convert_segment (xs, ys, xe, ye, min_y * supersample,
+                       min_scanlines, max_scanlines);
+
+      for (i = 1; i < poly_npts; i++)
+        {
+          xs = (gint) pts_tmp->x;
+          ys = (gint) pts_tmp->y;
+          pts_tmp++;
+          xe = (gint) pts_tmp->x;
+          ye = (gint) pts_tmp->y;
+
+          xs *= supersample;
+          ys *= supersample;
+          xe *= supersample;
+          ye *= supersample;
+
+          convert_segment (xs, ys, xe, ye, min_y * supersample,
+                           min_scanlines, max_scanlines);
+        }
+    }
+
+  vals = g_new (gfloat, size_x);
+  for (i = 0; i < size_y; i++, min_scanlines_iter++, max_scanlines_iter++)
+    {
+      if (! (i % supersample))
+        memset (vals, 0, sizeof (gfloat) * size_x);
+
+      yy = (gdouble) i / (gdouble) supersample + min_y;
+
+      for (j = *min_scanlines_iter; j < *max_scanlines_iter; j++)
+        {
+          x = j - min_x * supersample;
+          vals[x] += 1.0;
+        }
+
+      if (! ((i + 1) % supersample))
+        {
+          y = (i / supersample) + min_y;
+
+          if (y >= 0 && y < result->height)
+            {
+              for (j = 0; j < size_x; j += supersample)
+                {
+                  x = (j / supersample) + min_x;
+
+                  if (x >= 0 && x < result->width)
+                    {
+                      val = 0;
+
+                      for (k = 0; k < supersample; k++)
+                        val += vals[j + k];
+
+                      val /= supersample2;
+
+                      if (val > 0)
+                        {
+                          xx = (gdouble) j / (gdouble) supersample + min_x;
+
+                          contrib = calc_spec_contrib (mdatas->vecs, poly_npts,
+                                                       xx, yy, tile_rough,
+                                                       tile_height);
+
+                          for (b = 0; b < NB_CPN; b++)
+                            {
+                              pixel = col[b];
+
+                              if (contrib < 0.0)
+                                pixel += (col[b] - mdatas->back[b]) * contrib;
+                              else
+                                pixel += (mdatas->fore[b] - col[b]) * contrib;
+
+                              buf[b] = ((pixel * val) + (mdatas->back[b] * (1.0 - val)));
+
+                            }
+                          memcpy (output_buf + (y * result->width + x) * NB_CPN,
+                                  buf, NB_CPN * sizeof (gfloat));
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+  g_free (vals);
+  g_free (min_scanlines);
+  g_free (max_scanlines);
+}
+
+static void
+fill_poly_image (Polygon             *poly,
+                 gfloat              *input_buf,
+                 gfloat              *output_buf,
+                 gdouble              vary,
+                 const GeglRectangle *result,
+                 gboolean             antialiasing,
+                 gboolean             tile_rough,
+                 gdouble              tile_height,
+                 MosaicDatas         *mdatas)
+{
+  gdouble   dmin_x = 0.0, dmin_y = 0.0;
+  gdouble   dmax_x = 0.0, dmax_y = 0.0;
+  gint      xs, ys, xe, ye;
+  gint      min_x, min_y, max_x, max_y;
+  gint      size_x, size_y;
+  gint     *max_scanlines, *min_scanlines;
+  gfloat   *vals;
+  gfloat    val;
+  gfloat    pixel;
+  gfloat    buf[NB_CPN];
+  gint      b, i, j, k, x, y;
+  gdouble   contrib;
+  gdouble   xx, yy;
+  gint      supersample, supersample2;
+
+  /*  Determine antialiasing  */
+  if (antialiasing)
+    {
+      supersample = SUPERSAMPLE;
+      supersample2 = SQR (supersample);
+    }
+  else
+    {
+      supersample = supersample2 = 1;
+    }
+
+  for (i = 0; i < poly->npts; i++)
+    {
+      xs = (gint) ((i) ? poly->pts[i-1].x : poly->pts[poly->npts-1].x);
+      ys = (gint) ((i) ? poly->pts[i-1].y : poly->pts[poly->npts-1].y);
+      xe = (gint) poly->pts[i].x;
+      ye = (gint) poly->pts[i].y;
+
+      calc_spec_vec (mdatas->vecs+i, xs, ys, xe, ye,
+                     mdatas->light_x, mdatas->light_y);
+    }
+
+  polygon_extents (poly, &dmin_x, &dmin_y, &dmax_x, &dmax_y);
+
+  min_x = (gint) dmin_x;
+  min_y = (gint) dmin_y;
+  max_x = (gint) dmax_x;
+  max_y = (gint) dmax_y;
+
+  size_y = (max_y - min_y) * supersample;
+  size_x = (max_x - min_x) * supersample;
+
+  min_scanlines = g_new (gint, size_y);
+  max_scanlines = g_new (gint, size_y);
+
+  for (i = 0; i < size_y; i++)
+    {
+      min_scanlines[i] = max_x * supersample;
+      max_scanlines[i] = min_x * supersample;
+    }
+
+  for (i = 0; i < poly->npts; i++)
+    {
+      xs = (gint) ((i) ? poly->pts[i-1].x : poly->pts[poly->npts-1].x);
+      ys = (gint) ((i) ? poly->pts[i-1].y : poly->pts[poly->npts-1].y);
+      xe = (gint) poly->pts[i].x;
+      ye = (gint) poly->pts[i].y;
+
+      xs *= supersample;
+      ys *= supersample;
+      xe *= supersample;
+      ye *= supersample;
+
+      convert_segment (xs, ys, xe, ye, min_y * supersample,
+                       min_scanlines, max_scanlines);
+    }
+
+  vals = g_new (gfloat, size_x);
+
+  for (i = 0; i < size_y; i++)
+    {
+      if (! (i % supersample))
+        memset (vals, 0, sizeof (gfloat) * size_x);
+
+      yy = (gdouble) i / (gdouble) supersample + min_y;
+
+      for (j = min_scanlines[i]; j < max_scanlines[i]; j++)
+        {
+          x = j - min_x * supersample;
+          vals[x] += 1.0;
+        }
+
+      if (! ((i + 1) % supersample))
+        {
+          y = (i / supersample) + min_y;
+
+          if (y >= 0 && y < result->height)
+            {
+              for (j = 0; j < size_x; j += supersample)
+                {
+                  x = (j / supersample) + min_x;
+
+                  if (x >= 0 && x < result->width)
+                    {
+                      val = 0;
+                      for (k = 0; k < supersample; k++)
+                        val += vals[j + k];
+                      val /= supersample2;
+
+                      if (val > 0)
+                        {
+                          xx = (gdouble) j / (gdouble) supersample + min_x;
+
+                          contrib = calc_spec_contrib (mdatas->vecs,
+                                                       poly->npts, xx, yy,
+                                                       tile_rough, tile_height);
+
+                          memcpy (buf,
+                                  input_buf + (x + y*result->width) * NB_CPN,
+                                  NB_CPN * sizeof (gfloat));
+
+                          for (b = 0; b < NB_CPN; b++)
+                            {
+                              if (contrib < 0.0)
+                                pixel = buf[b] + (gint) ((buf[b] - mdatas->back[b]) * contrib);
+                              else
+                                pixel = buf[b] + (gint) ((mdatas->fore[b] - buf[b]) * contrib);
+
+                              /*  factor in per-tile intensity variation but alpha*/
+                              if (NB_CPN < 4)
+                                pixel += vary;
+
+                              pixel = CLAMP (pixel, 0.0, 1.0);
+
+                              buf[b] = (mdatas->back[b]) + (pixel - mdatas->back[b]) * val;
+                            }
+
+                          memcpy (output_buf + (y * result->width + x) * NB_CPN,
+                                  buf, NB_CPN * sizeof (gfloat));
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+  g_free (vals);
+  g_free (min_scanlines);
+  g_free (max_scanlines);
+}
+
+static void
+calc_spec_vec (SpecVec *vec,
+               gint     x1,
+               gint     y1,
+               gint     x2,
+               gint     y2,
+               gdouble  light_x,
+               gdouble  light_y)
+{
+  gdouble r;
+
+  vec->base_x = x1;
+  vec->base_y = y1;
+
+  r = sqrt (SQR (x2 - x1) + SQR (y2 - y1));
+
+  if (r > 0.0)
+    {
+      vec->norm_x = - (y2 - y1) / r;
+      vec->norm_y =   (x2 - x1) / r;
+    }
+  else
+    {
+      vec->norm_x = 0;
+      vec->norm_y = 0;
+    }
+
+  vec->light = vec->norm_x * light_x + vec->norm_y * light_y;
+}
+
+
+static double
+calc_spec_contrib (SpecVec *vecs,
+                   gint     n,
+                   gdouble  x,
+                   gdouble  y,
+                   gboolean tile_rough,
+                   gdouble  tile_height)
+{
+  gint i;
+  gdouble contrib = 0;
+
+  for (i = 0; i < n; i++)
+    {
+      gdouble x_p, y_p;
+      gdouble dist;
+
+      x_p = x - vecs[i].base_x;
+      y_p = y - vecs[i].base_y;
+
+      dist = fabs (x_p * vecs[i].norm_x + y_p * vecs[i].norm_y);
+
+      if (tile_rough)
+        {
+          /*  If the surface is rough, randomly perturb the distance  */
+          dist -= dist * g_random_double ();
+        }
+
+      /*  If the distance to an edge is less than the tile_spacing, there
+       *  will be no highlight as the tile blends to background here
+       */
+      if (dist < 1.0)
+        {
+          contrib += vecs[i].light;
+        }
+      else if (dist <= tile_height)
+        {
+          contrib += vecs[i].light * (1.0 - (dist / tile_height));
+        }
+    }
+
+  return contrib / 4.0;
+}
+
+static void
+convert_segment (gint  x1,
+                 gint  y1,
+                 gint  x2,
+                 gint  y2,
+                 gint  offset,
+                 gint *min,
+                 gint *max)
+{
+  gint    ydiff, y, tmp;
+  gdouble xinc, xstart;
+
+  if (y1 > y2)
+    {
+      tmp = y2; y2 = y1; y1 = tmp;
+      tmp = x2; x2 = x1; x1 = tmp;
+    }
+
+  ydiff = y2 - y1;
+
+  if (ydiff)
+    {
+      xinc = (gdouble) (x2 - x1) / (gdouble) ydiff;
+      xstart = x1 + 0.5 * xinc;
+
+      for (y = y1; y < y2; y++)
+        {
+          min[y - offset] = MIN (min[y - offset], xstart);
+          max[y - offset] = MAX (max[y - offset], xstart);
+
+          xstart += xinc;
+        }
+    }
+}
+
+static void
+polygon_add_point (Polygon *poly,
+                   gdouble  x,
+                   gdouble  y)
+{
+  if (poly->npts < 12)
+    {
+      poly->pts[poly->npts].x = x;
+      poly->pts[poly->npts].y = y;
+      poly->npts++;
+    }
+  else
+    {
+      g_warning ("can't add more points");
+    }
+}
+
+static gboolean
+polygon_find_center (Polygon *poly,
+                     gdouble *cx,
+                     gdouble *cy)
+{
+  guint i;
+
+  if (!poly->npts)
+    return FALSE;
+
+  *cx = 0.0;
+  *cy = 0.0;
+
+  for (i = 0; i < poly->npts; i++)
+    {
+      *cx += poly->pts[i].x;
+      *cy += poly->pts[i].y;
+    }
+
+  *cx /= poly->npts;
+  *cy /= poly->npts;
+
+  return TRUE;
+}
+
+static void
+polygon_translate (Polygon *poly,
+                   gdouble  tx,
+                   gdouble  ty)
+{
+  guint i;
+
+  for (i = 0; i < poly->npts; i++)
+    {
+      poly->pts[i].x += tx;
+      poly->pts[i].y += ty;
+    }
+}
+
+static void
+polygon_scale (Polygon *poly,
+               gdouble  poly_scale)
+{
+  guint i;
+
+  for (i = 0; i < poly->npts; i++)
+    {
+      poly->pts[i].x *= poly_scale;
+      poly->pts[i].y *= poly_scale;
+    }
+}
+
+static gboolean
+polygon_extents (Polygon *poly,
+                 gdouble *min_x,
+                 gdouble *min_y,
+                 gdouble *max_x,
+                 gdouble *max_y)
+{
+  guint i;
+
+  if (!poly->npts)
+    return FALSE;
+
+  *min_x = *max_x = poly->pts[0].x;
+  *min_y = *max_y = poly->pts[0].y;
+
+  for (i = 1; i < poly->npts; i++)
+    {
+      *min_x = MIN (*min_x, poly->pts[i].x);
+      *max_x = MAX (*max_x, poly->pts[i].x);
+      *min_y = MIN (*min_y, poly->pts[i].y);
+      *max_y = MAX (*max_y, poly->pts[i].y);
+    }
+
+  return TRUE;
+}
+
+static void
+polygon_reset (Polygon *poly)
+{
+  poly->npts = 0;
+}
+
+static void
+prepare (GeglOperation *operation)
+{
+  GeglOperationAreaFilter *area = GEGL_OPERATION_AREA_FILTER (operation);
+  GeglChantO              *o    = GEGL_CHANT_PROPERTIES (operation);
+
+  /* Should be slightly larger than needed */
+  area->left = area->right = 2 * ceil (o->tile_size) + 1;
+  area->top = area->bottom = 2 * ceil (o->tile_size) + 1;
+
+  gegl_operation_set_format (operation, "input",
+                             babl_format ("R'G'B'A float"));
+  gegl_operation_set_format (operation, "output",
+                             babl_format ("R'G'B'A float"));
+}
+
+static gboolean
+process (GeglOperation       *operation,
+         GeglBuffer          *input,
+         GeglBuffer          *output,
+         const GeglRectangle *result,
+         gint                 level)
+{
+
+  GeglRectangle            working_region;
+  GeglRectangle           *whole_region;
+  GeglOperationAreaFilter *area;
+  gfloat                  *res;
+  gint                     rowstride, offset;
+
+  area = GEGL_OPERATION_AREA_FILTER (operation);
+
+  whole_region = gegl_operation_source_get_bounding_box (operation, "input");
+
+  working_region.x      = result->x - area->left;
+  working_region.width  = result->width + area->left + area->right;
+  working_region.y      = result->y - area->top;
+  working_region.height = result->height + area->top + area->bottom;
+
+  /* Don't try to compute things outside the input extent,
+   * otherwise the behaviour is undefined */
+  gegl_rectangle_intersect (&working_region, &working_region, whole_region);
+
+  res = mosaic (operation, input, &working_region);
+
+  /* The region computed is larger than needed,
+   * so we extract the roi from the result of mosaic() */
+  rowstride = (working_region.width) * sizeof (gfloat) * 4;
+
+  offset  = (result->y - working_region.y) * working_region.width;
+  offset += (result->x - working_region.x);
+  offset *= 4;
+
+  gegl_buffer_set (output, result, 1.0, babl_format ("R'G'B'A float"),
+                   res + offset, rowstride);
+
+  g_free (res);
+
+  return TRUE;
+}
+
+static GeglRectangle
+get_bounding_box (GeglOperation *operation)
+{
+  GeglRectangle  result = { 0, 0, 0, 0 };
+  GeglRectangle *in_rect;
+
+  in_rect = gegl_operation_source_get_bounding_box (operation, "input");
+
+  if (in_rect)
+    {
+      result = *in_rect;
+    }
+
+  return result;
+}
+
+static void
+gegl_chant_class_init (GeglChantClass *klass)
+{
+  GeglOperationClass       *operation_class;
+  GeglOperationFilterClass *filter_class;
+
+  operation_class = GEGL_OPERATION_CLASS (klass);
+  filter_class    = GEGL_OPERATION_FILTER_CLASS (klass);
+
+  operation_class->prepare          = prepare;
+  operation_class->get_bounding_box = get_bounding_box;
+  filter_class->process             = process;
+
+  gegl_operation_class_set_keys (operation_class,
+    "name",        "gegl:mosaic",
+    "categories",  "artistic",
+    "description", _("Mosaic is a filter which transforms an image into "
+                     "what appears to be a mosaic, composed of small primitives, "
+                     "each of constant color and of an approximate size."),
+    NULL);
+}
+
+#endif


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]