[gegl] Work in progress to extract the seamless cloning into a standard API
- From: Michael Natterer <mitch src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gegl] Work in progress to extract the seamless cloning into a standard API
- Date: Tue, 14 May 2013 22:18:58 +0000 (UTC)
commit af8f2b0044af5e9d97fcf0cea3c1443c1cf34e26
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]