[gegl/soc-2013-opecl-ops] operations: add mosaic.c
- From: Carlos Zubieta <czubieta src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gegl/soc-2013-opecl-ops] operations: add mosaic.c
- Date: Mon, 23 Sep 2013 20:59:17 +0000 (UTC)
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]