[gegl/soc-2011-seamless-clone: 41/49] Work in progress to extract the seamless cloning into a standard API



commit 7738fe2b23e06a4ac21bb98738c786f9c2623f19
Author: Barak Itkin <lightningismyname gmail com>
Date:   Tue Sep 18 14:27:11 2012 +0300

    Work in progress to extract the seamless cloning into a standard API

 libs/seamless-clone/sc-common.h                    |   57 ++
 libs/seamless-clone/sc-context-private.h           |   47 ++
 libs/seamless-clone/sc-context.c                   |  639 ++++++++++++++++++++
 libs/seamless-clone/sc-context.h                   |  100 +++
 .../seamless-clone/sc-outline.c                    |    9 +-
 .../seamless-clone/sc-outline.h                    |    6 +-
 .../make-mesh.c => libs/seamless-clone/sc-sample.c |   76 +---
 libs/seamless-clone/sc-sample.h                    |   64 ++
 libs/seamless-clone/seamless-clone.h               |    4 +
 operations/common/seamless-clone/make-mesh.h       |   40 --
 .../common/seamless-clone/seamless-clone-common.c  |  309 ----------
 .../common/seamless-clone/seamless-clone-common.h  |   73 ---
 12 files changed, 921 insertions(+), 503 deletions(-)
---
diff --git a/libs/seamless-clone/sc-common.h b/libs/seamless-clone/sc-common.h
new file mode 100644
index 0000000..e0426d8
--- /dev/null
+++ b/libs/seamless-clone/sc-common.h
@@ -0,0 +1,57 @@
+/**
+ * The name of the Babl type used for working on colors.
+ * WARNING: THE CODE ASSUMES THAT NO MATTER WHAT THE FORMAT IS, THE
+ * ALPHA CHANNEL IS ALWAYS LAST. DO NOT CHANGE THIS WITHOUT UPDATING
+ * THE REST OF THE CODE!
+ */
+#define SC_COLOR_BABL_NAME     "R'G'B'A float"
+
+/**
+ * The type used for individual color channels
+ */
+typedef gfloat ScColorChannel;
+
+/**
+ * The amount of channels per color
+ */
+#define SC_COLORA_CHANNEL_COUNT 4
+#define SC_COLOR_CHANNEL_COUNT  ((SC_COLORA_CHANNEL_COUNT) - 1)
+
+/**
+ * The index of the alpha channel in the color
+ */
+#define SC_COLOR_ALPHA_INDEX   SC_COLOR_CHANNEL_COUNT
+
+/**
+ * The type used for a whole color
+ */
+typedef ScColorChannel ScColor[SC_COLORA_CHANNEL_COUNT];
+
+/**
+ * Apply a macro once for each non-alpha color channel, with the
+ * channel index as an input
+ */
+#define sc_color_process()  \
+G_STMT_START                \
+  sc_color_expr(0);         \
+  sc_color_expr(1);         \
+  sc_color_expr(2);         \
+G_STMT_END
+
+typedef struct {
+  GeglBuffer    *bg;
+  GeglRectangle *bg_rect;
+
+  GeglBuffer    *fg;
+  GeglRectangle *fg_rect;
+
+  gint           xoff;
+  gint           yoff;
+
+  gboolean       render_bg;
+} ScRenderInfo;
+
+typedef struct {
+  gint           x;
+  gint           y;
+} ScPoint;
diff --git a/libs/seamless-clone/sc-context-private.h b/libs/seamless-clone/sc-context-private.h
new file mode 100644
index 0000000..d364581
--- /dev/null
+++ b/libs/seamless-clone/sc-context-private.h
@@ -0,0 +1,47 @@
+/* This file is an image processing operation for GEGL
+ *
+ * sc-context-private.h
+ * Copyright (C) 2012 Barak Itkin <lightningismyname gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GEGL_SC_CONTEXT_PRIVATE_H__
+#define __GEGL_SC_CONTEXT_PRIVATE_H__
+
+typedef struct
+{
+  GHashTable     *pt2col;
+  ScRenderInfo   *info;
+  gboolean        is_valid;
+} ScRenderCache;
+
+struct ScContext_
+{
+  ScOutline      *outline;
+  GeglRectangle   mesh_bounds;
+  P2trMesh       *mesh;
+
+  ScMeshSampling *sampling;
+
+  gboolean        cache_uvt;
+  GeglBuffer     *uvt;
+
+  ScRenderCache  *render_cache;
+};
+
+#define SC_BABL_UVT_TYPE   (babl_type_new ("uvt", "bits", sizeof (P2trUVT) * 8, NULL))
+#define SC_BABL_UVT_FORMAT (babl_format_n (SC_BABL_UVT_TYPE, 1))
+
+#endif
diff --git a/libs/seamless-clone/sc-context.c b/libs/seamless-clone/sc-context.c
new file mode 100644
index 0000000..0f0f864
--- /dev/null
+++ b/libs/seamless-clone/sc-context.c
@@ -0,0 +1,639 @@
+/* This file is an image processing operation for GEGL
+ *
+ * seamless-clone-common.c
+ * Copyright (C) 2012 Barak Itkin <lightningismyname gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "seamless-clone-common.h"
+#include "make-mesh.h"
+#include <gegl-utils.h>
+
+static ScOutline*  sc_context_create_outline (GeglBuffer          *input,
+                                              const GeglRectangle *roi,
+                                              gdouble              threshold,
+                                              ScCreationError     *error)
+
+static P2trMesh*   sc_make_fine_mesh         (ScOutline           *outline,
+                                              GeglRectangle       *mesh_bounds,
+                                              int                  max_refine_steps);
+
+static GeglBuffer* sc_compute_uvt_cache      (P2trMesh            *mesh,
+                                              const GeglRectangle *area);
+
+
+static gboolean sc_context_render_cache_pt2col_update (ScContext *context);
+
+static void     sc_context_render_cache_pt2col_free   (ScContext *context);
+
+ScContext*
+sc_context_new (GeglBuffer          *input,
+                const GeglRectangle *roi,
+                gdouble              threshold,
+                ScCreationError     *error)
+{
+  ScOutline *outline;
+  guint      outline_length;
+  ScContext *self;
+
+  outline = sc_context_create_outline (input, roi, threshold, error);
+
+  if (outline == NULL)
+    return NULL;
+
+  self           = g_slice_new (ScContext);
+  outline_length = sc_outline_length (outline);
+
+  self->outline      = outline;
+  self->mesh         = sc_make_fine_mesh (self->outline,
+                                          &self->mesh_bounds,
+                                          5 * outline_length);
+  self->samplig      = sc_mesh_sampling_compute (self->outline,
+                                                 self->mesh);
+  self->cache_uvt    = FALSE;
+  self->uvt          = NULL;
+  self->render_cache = NULL;
+
+  return self;
+}
+
+static ScContext*
+sc_context_create_outline (GeglBuffer          *input,
+                           const GeglRectangle *roi,
+                           gdouble              threshold,
+                           ScCreationError     *error)
+{
+  gboolean   ignored_islands = FALSE;
+  ScOutline *outline = sc_outline_find (roi, input, &ignored_islands);
+  guint      length  = sc_outline_length (outline);
+
+  if (length == 0)
+    {
+      if (ignored_islands)
+        *error = SC_CREATION_ERROR_TOO_SMALL;
+      else
+        *error = SC_CREATION_ERROR_EMPTY;
+    }
+  /* In order to create a triangular mesh, we need at least 3 vertices.
+   * Also, if we don't have 3 vertices then the area is so small that it
+   * will be blended into the background anyhow
+   */
+  else if (length < 3)
+    {
+      *error = SC_CREATION_ERROR_TOO_SMALL;
+    }
+  else if (ignored_islands ||
+           ! sc_outline_check_if_single (extents, fg, result->outline))
+    {
+      *error = SC_CREATION_ERROR_HOLED_OR_SPLIT;
+    }
+
+  if (*error != SC_CREATION_ERROR_NONE)
+    {
+      sc_outline_free (outline);
+      outline = NULL;
+    }
+
+  return outline;
+}
+
+/**
+ * sc_make_fine_mesh:
+ * @outline: An ScOutline object describing the PSLG of the mesh
+ * @mesh_bounds: A rectangle in which the bounds of the mesh should be
+ *               stored
+ */
+static P2trMesh*
+sc_make_fine_mesh (ScOutline     *outline,
+                   GeglRectangle *mesh_bounds,
+                   int            max_refine_steps)
+{
+  GPtrArray *realOutline = (GPtrArray*) outline;
+  gint i, N = realOutline->len;
+  gint min_x = G_MAXINT, max_x = -G_MAXINT, min_y = G_MAXINT, max_y = -G_MAXINT;
+
+  /* An array of P2tPoint*, holding the outline points */
+  GPtrArray *mesh_points = g_ptr_array_new ();
+
+  P2tCDT *rough_cdt;
+  P2trCDT *fine_cdt;
+  P2trMesh *result;
+  P2trRefiner *refiner;
+
+  for (i = 0; i < N; i++)
+    {
+      ScPoint *pt = (ScPoint*) g_ptr_array_index (realOutline, i);
+      gdouble realX = pt->x + SC_DIRECTION_XOFFSET (pt->outside_normal, 0.25);
+      gdouble realY = pt->y + SC_DIRECTION_YOFFSET (pt->outside_normal, 0.25);
+
+      min_x = MIN (realX, min_x);
+      min_y = MIN (realY, min_y);
+      max_x = MAX (realX, max_x);
+      max_y = MAX (realY, max_y);
+
+      /* No one should care if the points are given in reverse order,
+       * and prepending to the GList is more efficient */
+      g_ptr_array_add (mesh_points, p2t_point_new_dd (realX, realY));
+    }
+
+  mesh_bounds->x = min_x;
+  mesh_bounds->y = min_y;
+  mesh_bounds->width = max_x + 1 - min_x;
+  mesh_bounds->height = max_y + 1 - min_y;
+
+  rough_cdt = p2t_cdt_new (mesh_points);
+  p2t_cdt_triangulate (rough_cdt);
+  fine_cdt = p2tr_cdt_new (rough_cdt);
+  /* We no longer need the rough CDT */
+  p2t_cdt_free (rough_cdt);
+
+  refiner = p2tr_refiner_new (G_PI / 6, p2tr_refiner_false_too_big, fine_cdt);
+  p2tr_refiner_refine (refiner, max_refine_steps, NULL);
+  p2tr_refiner_free (refiner);
+
+  p2tr_mesh_ref (result = fine_cdt->mesh);
+
+  p2tr_cdt_free_full (fine_cdt, FALSE);
+
+  for (i = 0; i < N; i++)
+    {
+      p2t_point_free ((P2tPoint*) g_ptr_array_index (mesh_points, i));
+    }
+
+  g_ptr_array_free (mesh_points, TRUE);
+
+  return result;
+}
+
+gboolean
+sc_context_prepare_render (ScContext    *context,
+                           ScRenderInfo *info)
+{
+  if (context->render_cache == NULL)
+    context->render_cache = g_slice_new (ScRenderCache);
+
+  context->render_cache->is_valid = FALSE;
+
+  context->render_cache->info = info;
+
+  if (! sc_context_render_cache_pt2col_update (context))
+    return FALSE;
+
+  if (context->cache_uvt && context->uvt == NULL)
+    context->uvt = sc_compute_uvt_cache (context->mesh, info->fg_rect);
+
+  context->render_cache->is_valid = TRUE;
+
+  return TRUE;
+}
+
+/**
+ * Compute the color assigned to all the points in the color difference
+ * mesh. If the color can not be computed for one or more points (due to
+ * any of the reasons documented in sc_context_sample_point), this
+ * function will return FALSE - meaning a failure.
+ * IT IS THE CALLERS RESPONSIBILITY TO DETECT SUCH A STATE AND STOP THE
+ * RENDERING PROCESS!
+ */
+static gboolean
+sc_context_render_cache_pt2col_update (ScContext *context)
+{
+  GHashTableIter iter;
+
+  ScColor      *color_current = NULL;
+  P2trPoint    *pt            = NULL;
+  ScSampleList *sl            = NULL;
+  GHashTable   *pt2col;
+
+  /* If this is the first time we compute the colors, we need to
+   * allocate the color map */
+  if (context->render_cache->pt2col == NULL)
+    {
+      pt2col = g_hash_table_new (g_direct_hash, g_direct_equal);
+      context->render_cache->pt2col = pt2col;
+    }
+  else
+    {
+      pt2col = context->render_cache->pt2col;
+    }
+
+  /* The points in the map and in the mesh, may be in one of 3 states
+   * (ordered by likelihood due to the current implementation):
+   * 1. The point is in the map and in the mesh, we just need to update
+   *    the color sampled for it
+   * 2. The point is not in the map but is in the mesh (a new point), we
+   *    need to simply update the map here with a new sample
+   * 3. The point exists in the map but no longer exists in the mesh (a
+   *    deleted point), we would want to remove it from the mesh
+   */
+
+  /* Iterate over the current sampling */
+  g_hash_table_iter_init (&iter, context->sampling);
+  while (g_hash_table_iter_next (&iter,
+                                 (gpointer*) &pt,
+                                 (gpointer*) &sl))
+    {
+      /* See if we have a pt2col entry for this point? */
+      if (! g_hash_table_lookup_extended (pt2col, pt, NULL,
+                                          &color_current))
+        {
+          color_current = g_slice_new (ScColor);
+          g_hash_table_insert (pt2col,
+                               p2tr_point_ref (pt),
+                               color_current);
+        }
+
+      /* Now, actually find the color for this point and update it
+       * directly in the color buffer (which is already held in the
+       * mapping).
+       *
+       * Note that we first insert the allocated color and reffed point,
+       * and only then we allow ourselves to fail. If we would fail
+       * after allocating/reffing but before inserting, we would have a
+       * memory leak!
+       */
+      if (! sc_context_sample_point (sl, info, pt, color_current))
+        return FALSE;
+    }
+
+  /* Now, lets see if there were any additional points in the mapping, that
+   * we should remove now */
+  if (g_hash_table_size (context->sampling) < g_hash_table_size (pt2col))
+    {
+      /* Iterate over the color mapping */
+      g_hash_table_iter_init (&iter, pt2col);
+      while (g_hash_table_iter_next (&iter,
+                                    (gpointer*) &pt,
+                                    (gpointer*) &color_current))
+        {
+          /* See if we have a sampling entry for this point? */
+          if (! g_hash_table_lookup_extended (context->sampling, pt, NULL, NULL))
+            {
+              g_slice_free (ScColor, color_current);
+              g_hash_table_iter_remove (&iter);
+              p2tr_point_unref (pt);
+            }
+        }
+    }
+
+  return TRUE;
+}
+
+/**
+ * Compute the color assigned to a given point in the color difference
+ * mesh. If the color can not be computed since all the sample points
+ * assigned to this mesh point are outside the background, this function
+ * will return FALSE - meaning a failure.
+ * IT IS THE CALLERS RESPONSIBILITY TO DETECT SUCH A STATE AND STOP THE
+ * RENDERING PROCESS!
+ */
+static gboolean
+sc_context_sample_point (ScRenderInfo *info,
+                         ScSampleList *sl,
+                         P2trPoint    *point,
+                         ScColor      *dest)
+{ 
+  gint          i;
+  gdouble       weightT = 0;
+  guint         N       = sl->points->len;
+
+  const Babl   *format  = babl_format (SC_COLOR_BABL_NAME);
+
+  gfloat fg_c[SC_COLORA_CHANNEL_COUNT];
+  gfloat bg_c[SC_COLORA_CHANNEL_COUNT];
+
+  /* We don't need an alpha for this one */
+  gfloat dest_c[SC_COLOR_CHANNEL_COUNT]  = { 0 };
+
+  for (i = 0; i < N; i++)
+    {
+      ScPoint *pt = g_ptr_array_index (sl->points, i);
+      gdouble weight = g_array_index (sl->weights, gdouble, i);
+
+      /* If the outline point is outside the background, then we can't
+       * compute a propper difference there. So, don't add it to the
+       * sampling.
+       *
+       * The outline point obviously must lie inside the foreground, so
+       * we don't have anything to worry about here.
+       *
+       * The only thing we should notice, is to use the right
+       * coordinates: The original outline point is on (pt->x,pt->y) in
+       * terms of mesh coordinates. But, since we are translating, its
+       * location in background coordinates is the following:
+       * (pt->x + info->x, pt->y + info->y).
+       */
+#define sc_rect_contains(rect,x0,y0) \
+     (((x0) >= (rect).x) && ((x0) < (rect).x + (rect).width)  \
+   && ((y0) >= (rect).y) && ((y0) < (rect).y + (rect).height))
+
+      if (! sc_rect_contains (info->bg_rect,
+                              pt->x + info->xoff,
+                              pt->y + info->yoff))
+        {
+          continue;
+        }
+
+#undef sc_rect_contains
+
+      gegl_buffer_sample (cci->fg,
+                          pt->x, pt->y,
+                          NULL, fg_c, format,
+                          GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+
+      /* Sample the BG with the offset */
+      gegl_buffer_sample (cci->bg,
+                          pt->x + info->xoff, pt->y + info->yoff,
+                          NULL, input_c, format,
+                          GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+
+#define sc_color_expr(I)  dest_c[I] += weight * (input_c[I] - aux_c[I]) 
+      sc_color_process();
+#undef  sc_color_expr
+      weightT += weight;
+    }
+
+#define sc_color_expr(I)  dest[I] = dest_c[I] / weightT
+  sc_color_process();
+#undef  sc_color_expr
+  dest[SC_COLOR_ALPHA_INDEX] = 1;
+}
+
+static void
+sc_context_render_cache_pt2col_free (ScContext *context)
+{
+  if (context->render_cache->pt2col == NULL)
+    return;
+
+  g_hash_table_iter_init (&iter, context->render_cache->pt2col);
+  while (g_hash_table_iter_next (&iter,
+                                (gpointer*) &pt,
+                                (gpointer*) &color_current))
+    {
+      g_slice_free (ScColor, color_current);
+      g_hash_table_iter_remove (&iter);
+      p2tr_point_unref (pt);
+    }
+
+  g_hash_table_destroy (context->render_cache->pt2col);
+
+  context->pt2col = NULL;
+}
+
+static GeglBuffer*
+sc_compute_uvt_cache (P2trMesh            *mesh,
+                      const GeglRectangle *area)
+{
+  GeglBuffer         *uvt;
+  GeglBufferIterator *iter;
+  P2trImageConfig     config;
+
+  uvt = gegl_buffer_new (area, SC_BABL_UVT_FORMAT);
+
+  iter = gegl_buffer_iterator_new (uvt, area, 0, SC_BABL_UVT_FORMAT,
+                                   GEGL_BUFFER_WRITE, GEGL_ABYSS_NONE);
+
+  config.step_x = config.step_y = 1;
+  config.cpp = SC_COLOR_CHANNEL_COUNT; /* Not that it will be used, but it won't harm */
+
+  while (gegl_buffer_iterator_next (iter))
+    {
+      config. min_x = iter->roi[0].x;
+      config.min_y = iter->roi[0].y;
+      config.x_samples = iter->roi[0].width;
+      config.y_samples = iter->roi[0].height;
+      p2tr_mesh_render_cache_uvt_exact (mesh,
+                                        (P2trUVT*) iter->data[0],
+                                        iter->length,
+                                        &config);
+    }
+
+  /* No need to free the iterator */
+
+  return uvt;
+}
+
+void
+sc_context_set_uvt_cache (ScContext *context,
+                          gboolean   enabled)
+{
+  context->cache_uvt = enabled;
+  if (! enabled && context->uvt != NULL)
+    {
+      g_object_unref (context->uvt);
+      context->uvt = NULL;
+    }
+}
+
+void
+sc_context_render (ScContext           *context,
+                   const GeglRectangle *part_rect,
+                   GeglBuffer          *part)
+{
+  /** The area filled by the FG buf after the offset */
+  GeglRectangle fg_rect;
+  /** The intersection of fg_rect and the area that we should output */
+  GeglRectangle to_render;
+  /** The area matching to_render in the FG buf without the offset */
+  GeglRectangle to_render_fg;
+
+  GeglBufferIterator *iter;
+  gint out_index, uvt_index, fg_index;
+  gint xoff, yoff;
+
+  const Babl *format = babl_format (SC_COLOR_BABL_NAME);
+
+  if (context->render_cache == NULL)
+    {
+      g_warning ("No preprocessing result given. Stop.");
+      return FALSE;
+    }
+
+  if (! context->render_cache->is_valid)
+    {
+      g_warning ("The preprocessing result contains an error. Stop.");
+      return FALSE;
+    }
+
+  if (gegl_rectangle_is_empty (&context->mesh_bounds))
+    {
+      return TRUE;
+    }
+
+  if (! gegl_rectangle_contains (&context->render_cache->info->fg_rect,
+                                 &context->mesh_bounds))
+    {
+      g_warning ("The mesh from the preprocessing is not inside the "
+          "foreground. Stop");
+      return FALSE;
+    }
+
+  xoff = context->render_cache->info->xoff;
+  yoff = context->render_cache->info->yoff;
+
+  /* The real rectangle of the foreground that we should render is
+   * defined by the bounds of the mesh plus the given offset */
+  gegl_rectangle_set (&fg_rect,
+      context->mesh_bounds.x + xoff,
+      context->mesh_bounds.y + yoff,
+      context->mesh_bounds.width,
+      context->mesh_bounds.height);
+
+  /* We only need to render the intersection of the mesh bounds and the
+   * desired output */
+  gegl_rectangle_intersect (&to_render, part_rect, &fg_rect);
+
+  if (gegl_rectangle_is_empty (&to_render))
+    {
+      return TRUE;
+    }
+
+  /* Render the mesh into it */
+
+  /* Iterate over the output buffer, while synching with the paste and
+   * the cache */
+  iter      = gegl_buffer_iterator_new (part,
+                                        &to_render,
+                                        0,
+                                        format,
+                                        GEGL_BUFFER_WRITE,
+                                        GEGL_ABYSS_NONE);
+  out_index = 0;
+  
+  gegl_rectangle_set (&to_render_fg,
+      to_render.x - fg_xoff, to_render.y - fg_yoff,
+      to_render.width,       to_render.height);
+
+  if (context->uvt)
+    {
+      uvt_index = gegl_buffer_iterator_add (iter,
+                                            context->uvt,
+                                            &to_render_fg,
+                                            0,
+                                            SC_BABL_UVT_FORMAT,
+                                            GEGL_BUFFER_READ,
+                                            GEGL_ABYSS_NONE);
+    }
+  else
+    {
+      uvt_index = -1;
+    }
+
+  fg_index  = gegl_buffer_iterator_add (iter,
+                                        context->render_cache->info->fg,
+                                        &to_render_fg,
+                                        0,
+                                        format,
+                                        GEGL_BUFFER_READ,
+                                        GEGL_ABYSS_NONE);
+
+  while (gegl_buffer_iterator_next (iter))
+    {
+      P2trImageConfig  imcfg;
+      float           *out_raw, *fg_raw;
+      P2trUVT         *uvt_raw;
+      int              x, y;
+      
+      imcfg.min_x = iter->roi[fg_index].x;
+      imcfg.min_y = iter->roi[fg_index].y;
+      imcfg.step_x = imcfg.step_y = 1;
+      imcfg.x_samples = iter->roi[fg_index].width;
+      imcfg.y_samples = iter->roi[fg_index].height;
+      /* This is without the alpha! */
+      imcfg.cpp = SC_COLOR_CHANNEL_COUNT;
+      /* WARNING: This must be synched with SC_COLOR_BABL_NAME!!! */
+      imcfg.alpha_last = TRUE;
+
+      out_raw = (gfloat*)iter->data[out_index];
+      fg_raw = (gfloat*)iter->data[fg_index];
+      if (uvt_index != -1)
+        uvt_raw = (P2trUVT*)iter->data[uvt_index];
+
+      if (uvt_index != -1)
+        {
+          p2tr_mesh_render_from_cache_f (uvt_raw,
+                                         out_raw, iter->length,
+                                         &imcfg,
+                                         sc_point_to_color_func,
+                                         context->render_cache->pt2col);
+        }
+      else
+        {
+          p2tr_mesh_render_f (context->mesh,
+                              out_raw,
+                              &imcfg,
+                              sc_point_to_color_func,
+                              context->render_cache->pt2col);
+        }
+
+      for (y = 0; y < imcfg.y_samples; y++)
+        {
+          for (x = 0; x < imcfg.x_samples; x++)
+            {
+#define sc_color_expr(I)  out_raw[I] += fg_raw[I]
+              sc_color_process();
+#undef  sc_color_expr
+              out_raw += SC_COLORA_CHANNEL_COUNT;
+              fg_raw += SC_COLORA_CHANNEL_COUNT;
+            }
+        }
+    }
+  
+  return TRUE;
+}
+
+static void
+sc_point_to_color_func (P2trPoint *point,
+                        gfloat    *dest,
+                        gpointer   pt2col_p)
+{
+  GHashTable *pt2col  = (GHashTable*) pt2col_p;
+  gfloat     *col_cpy = g_hash_table_lookup (cci->pt2col, point);
+  guint       i;
+
+  g_assert (col_cpy != NULL);
+
+  for (i = 0; i < SC_COLORA_CHANNEL_COUNT; ++i)
+    dest[i] = col_cpy[i];
+}
+
+static void
+sc_context_render_cache_free (ScContext *context)
+{
+  if (context->render_cache == NULL)
+    return;
+
+  sc_context_render_cache_pt2col_free (context);
+  g_slice_free (ScRenderCache, context->render_cache);
+  context->render_cache = NULL;
+}
+
+void
+sc_context_free (ScCotnext *context)
+{
+  if (context->render_cache)
+    sc_context_render_cache_free (context);
+
+  if (context->uvt != NULL)
+    g_object_unref (context->uvt);
+
+  sc_mesh_sampling_free (context->sampling);
+  p2tr_mesh_unref (context->mesh);
+  sc_outline_free (context->outline);
+
+  g_slice_free (ScContext, context);
+  
+}
+
diff --git a/libs/seamless-clone/sc-context.h b/libs/seamless-clone/sc-context.h
new file mode 100644
index 0000000..f501e5b
--- /dev/null
+++ b/libs/seamless-clone/sc-context.h
@@ -0,0 +1,100 @@
+/*
+ * This file is an image processing operation for GEGL
+ * sc-context.h
+ * Copyright (C) 2012 Barak Itkin <lightningismyname gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GEGL_SC_CONTEXT_H__
+#define __GEGL_SC_CONTEXT_H__
+
+#include <poly2tri-c/p2t/poly2tri.h>
+#include <poly2tri-c/refine/refine.h>
+#include <poly2tri-c/render/mesh-render.h>
+
+#include "sc-common.h"
+
+/**
+ * An opaque type representing the context for a seamless cloning
+ * operation
+ */
+typedef struct _ScContext ScContext;
+
+/**
+ * Errors generated during the creation of a seamless cloning context
+ */
+typedef enum {
+  /**
+   * No error
+   */
+  SC_CREATION_ERROR_NONE = 0,
+  /**
+   * The input doesn't contain an opaque area
+   */
+  SC_CREATION_ERROR_EMPTY,
+  /**
+   *The opaque area of the input is too small
+   */
+  SC_CREATION_ERROR_TOO_SMALL,
+  /**
+   * The opaque area of the input either contains holes or is split
+   * to several disconnected areas
+   */
+  SC_CREATION_ERROR_HOLED_OR_SPLIT
+} ScCreationError;
+
+/**
+ * Create a new seamless cloning context where the alpha of the
+ * given input buffer will be used to determine its outline.
+ */
+ScContext* sc_context_new            (GeglBuffer          *input,
+                                      const GeglRectangle *roi,
+                                      gdouble              threshold,
+                                      ScCreationError     *error);
+
+/**
+ * Do the necessary caching so that rendering can happen. This function
+ * is not thread-safe, and must be called before each call to the
+ * render function (unless none of the ScRenderInfo parts was changed).
+ * If this function returns FALSE, it means that a rendering can not be
+ * created in the current setup. CURRENTLY THE ONLY REASON FOR SUCH A
+ * BEHAVIOUR IS THAT THE FOREGROUND DOES NOT OVERLAP ENOUGH THE
+ * BACKGROUND!
+ */
+gboolean       sc_context_prepare_render (ScContext       *context,
+                                          ScRenderInfo    *info);
+
+/**
+ * Specifies whether the triangle containing each pixel, along with the
+ * UV coordinates of the pixel, should be cached. This requires a memory
+ * buffer which is linear in the area of the outline, but it allows to
+ * render the result faster.
+ * This function takes effect from the next call to prepare_render.
+ */
+void       sc_context_set_uvt_cache  (ScContext           *context,
+                                      gboolean             enabled);
+
+/**
+ * Call this function to render the specified area of the seamless
+ * cloning composition. This call must be preceeded by a call to
+ * the prepare function.
+ */
+void       sc_context_render         (ScContext           *context,
+                                      const GeglRectangle *part_rect,
+                                      GeglBuffer          *part);
+
+void       sc_context_free           (ScContext           *context);
+
+#endif
diff --git a/operations/common/seamless-clone/find-outline.c b/libs/seamless-clone/sc-outline.c
similarity index 98%
rename from operations/common/seamless-clone/find-outline.c
rename to libs/seamless-clone/sc-outline.c
index 69efad0..f016897 100644
--- a/operations/common/seamless-clone/find-outline.c
+++ b/libs/seamless-clone/sc-outline.c
@@ -1,6 +1,6 @@
 /* This file is an image processing operation for GEGL
  *
- * find-outline.c
+ * sc-outline.c
  * Copyright (C) 2011 Barak Itkin <lightningismyname gmail com>
  *
  * This program is free software: you can redistribute it and/or modify
@@ -40,10 +40,7 @@
  */
 
 #include <gegl.h>
-
-#include "seamless-clone.h"
-#include <poly2tri-c/p2t/poly2tri.h>
-#include <poly2tri-c/refine/refine.h>
+#include "sc-outline.h"
 
 static inline void
 sc_point_copy_to (const ScPoint *src,
@@ -290,7 +287,7 @@ sc_point_cmp (const ScPoint **pt1,
  * Since our scan of the image is by rows (increasing X) and going from
  * top to bottom, this will exactly match the sorting of the array,
  * allowing to check whether a pixel is in the outline in constant
- * time!
+ * (amortized) time!
  */
 gboolean
 sc_outline_check_if_single (const GeglRectangle *search_area,
diff --git a/operations/common/seamless-clone/find-outline.h b/libs/seamless-clone/sc-outline.h
similarity index 97%
rename from operations/common/seamless-clone/find-outline.h
rename to libs/seamless-clone/sc-outline.h
index cc56379..4da83ad 100644
--- a/operations/common/seamless-clone/find-outline.h
+++ b/libs/seamless-clone/sc-outline.h
@@ -1,6 +1,6 @@
 /* This file is an image processing operation for GEGL
  *
- * find-outline.h
+ * sc-outline.h
  * Copyright (C) 2011 Barak Itkin <lightningismyname gmail com>
  *
  * This program is free software: you can redistribute it and/or modify
@@ -17,8 +17,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef __SEAMLESS_CLONE_FIND_OUTLINE_H__
-#define __SEAMLESS_CLONE_FIND_OUTLINE_H__
+#ifndef __GEGL_SC_OUTLINE_H__
+#define __GEGL_SC_OUTLINE_H__
 
 #include <gegl.h>
 
diff --git a/operations/common/seamless-clone/make-mesh.c b/libs/seamless-clone/sc-sample.c
similarity index 75%
rename from operations/common/seamless-clone/make-mesh.c
rename to libs/seamless-clone/sc-sample.c
index 176ecb1..be366fd 100644
--- a/operations/common/seamless-clone/make-mesh.c
+++ b/libs/seamless-clone/sc-sample.c
@@ -23,12 +23,11 @@
  * inner point of the mesh will be defined.
  */
 
-#include <gegl.h>
-#include <stdio.h> /* TODO: get rid of this debugging way! */
-
-#include <poly2tri-c/p2t/poly2tri.h>
+#include <glib.h>
 #include <poly2tri-c/refine/refine.h>
-#include "seamless-clone.h"
+
+#include "sc-outline.h"
+#include "sc-sample.h"
 
 #define g_ptr_array_index_cyclic(array,index_) g_ptr_array_index(array,(index_)%((array)->len))
 
@@ -221,70 +220,3 @@ sc_mesh_sampling_free (ScMeshSampling *self)
   g_hash_table_destroy (real);
 }
 
-/**
- * sc_make_fine_mesh:
- * @outline: An ScOutline object describing the PSLG of the mesh
- * @mesh_bounds: A rectangle in which the bounds of the mesh should be
- *               stored
- */
-P2trMesh*
-sc_make_fine_mesh (ScOutline     *outline,
-                   GeglRectangle *mesh_bounds,
-                   int            max_refine_steps)
-{
-  GPtrArray *realOutline = (GPtrArray*) outline;
-  gint i, N = realOutline->len;
-  gint min_x = G_MAXINT, max_x = -G_MAXINT, min_y = G_MAXINT, max_y = -G_MAXINT;
-
-  /* An array of P2tPoint*, holding the outline points */
-  GPtrArray *mesh_points = g_ptr_array_new ();
-
-  P2tCDT *rough_cdt;
-  P2trCDT *fine_cdt;
-  P2trMesh *result;
-  P2trRefiner *refiner;
-
-  for (i = 0; i < N; i++)
-    {
-      ScPoint *pt = (ScPoint*) g_ptr_array_index (realOutline, i);
-      gdouble realX = pt->x + SC_DIRECTION_XOFFSET (pt->outside_normal, 0.25);
-      gdouble realY = pt->y + SC_DIRECTION_YOFFSET (pt->outside_normal, 0.25);
-
-      min_x = MIN (realX, min_x);
-      min_y = MIN (realY, min_y);
-      max_x = MAX (realX, max_x);
-      max_y = MAX (realY, max_y);
-
-      /* No one should care if the points are given in reverse order,
-       * and prepending to the GList is more efficient */
-      g_ptr_array_add (mesh_points, p2t_point_new_dd (realX, realY));
-    }
-
-  mesh_bounds->x = min_x;
-  mesh_bounds->y = min_y;
-  mesh_bounds->width = max_x + 1 - min_x;
-  mesh_bounds->height = max_y + 1 - min_y;
-
-  rough_cdt = p2t_cdt_new (mesh_points);
-  p2t_cdt_triangulate (rough_cdt);
-  fine_cdt = p2tr_cdt_new (rough_cdt);
-  /* We no longer need the rough CDT */
-  p2t_cdt_free (rough_cdt);
-
-  refiner = p2tr_refiner_new (G_PI / 6, p2tr_refiner_false_too_big, fine_cdt);
-  p2tr_refiner_refine (refiner, max_refine_steps, NULL);
-  p2tr_refiner_free (refiner);
-
-  p2tr_mesh_ref (result = fine_cdt->mesh);
-
-  p2tr_cdt_free_full (fine_cdt, FALSE);
-
-  for (i = 0; i < N; i++)
-    {
-      p2t_point_free ((P2tPoint*) g_ptr_array_index (mesh_points, i));
-    }
-
-  g_ptr_array_free (mesh_points, TRUE);
-
-  return result;
-}
diff --git a/libs/seamless-clone/sc-sample.h b/libs/seamless-clone/sc-sample.h
new file mode 100644
index 0000000..6d3373b
--- /dev/null
+++ b/libs/seamless-clone/sc-sample.h
@@ -0,0 +1,64 @@
+/* This file is an image processing operation for GEGL
+ *
+ * sc-sample.h
+ * Copyright (C) 2011 Barak Itkin <lightningismyname gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GEGL_SC_SAMPLE_H__
+#define __GEGL_SC_SAMPLE_H__
+
+#include <glib.h>
+#include <poly2tri/refine/refine.h>
+
+#include "sc-outline.h"
+
+typedef struct {
+  /** An array of ScPoint* (pointers) of the points to sample       */
+  GPtrArray *points;
+  /** An array of weights to assign to the samples from the points  */
+  GArray    *weights;
+  /** The total weight of the samples, used to normalize the result */
+  gdouble    total_weight;
+} ScSampleList;
+
+typedef GHashTable ScMeshSampling;
+
+/**
+ * Compute the list of points that should be sampled in order to
+ * compute the color assigned to the given point in the color
+ * difference mesh.
+ */
+ScSampleList*   sc_sample_list_compute   (ScOutline      *outline,
+                                          gdouble         x,
+                                          gdouble         y);
+
+/**
+ * Free an ScSampleList object created by sc_sample_list_compute
+ */
+void            sc_sample_list_free      (ScSampleList   *self);
+
+/**
+ * Compute the sample lists for all the points in a given mesh
+ */
+ScMeshSampling* sc_mesh_sampling_compute (ScOutline      *outline,
+                                          P2trMesh       *mesh);
+
+/**
+ * Free an ScMeshSampling object created by sc_mesh_sampling_compute
+ */
+void            sc_mesh_sampling_free    (ScMeshSampling *self);
+
+#endif
diff --git a/libs/seamless-clone/seamless-clone.h b/libs/seamless-clone/seamless-clone.h
new file mode 100644
index 0000000..eef19d8
--- /dev/null
+++ b/libs/seamless-clone/seamless-clone.h
@@ -0,0 +1,4 @@
+make-mesh.h
+sc-context.h
+sc-context-private.h
+sc-outline.h



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