Commit 7738fe2b authored by Barak Itkin's avatar Barak Itkin
Browse files

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

parent 1aebc06e
/**
* 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;
/* This file is an image processing operation for GEGL
*
* make-mesh.h
* Copyright (C) 2011 Barak Itkin <lightningismyname@gmail.com>
* 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
......@@ -17,24 +17,31 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __SEAMLESS_CLONE_MAKE_MESH_H__
#define __SEAMLESS_CLONE_MAKE_MESH_H__
#ifndef __GEGL_SC_CONTEXT_PRIVATE_H__
#define __GEGL_SC_CONTEXT_PRIVATE_H__
typedef struct {
GPtrArray *points; /* An array of ScPoint* objects */
GArray *weights; /* An array of gdouble values */
gdouble total_weight;
} ScSampleList;
typedef struct
{
GHashTable *pt2col;
ScRenderInfo *info;
gboolean is_valid;
} ScRenderCache;
typedef GHashTable ScMeshSampling;
struct ScContext_
{
ScOutline *outline;
GeglRectangle mesh_bounds;
P2trMesh *mesh;
ScSampleList* sc_sample_list_compute (ScOutline *outline, gdouble Px, gdouble Py);
void sc_sample_list_free (ScSampleList *self);
ScMeshSampling *sampling;
ScMeshSampling* sc_mesh_sampling_compute (ScOutline *outline, P2trMesh *mesh);
void sc_mesh_sampling_free (ScMeshSampling *self);
gboolean cache_uvt;
GeglBuffer *uvt;
P2trMesh* sc_make_fine_mesh (ScOutline *outline, GeglRectangle *mesh_bounds, int max_refine_steps);
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
This diff is collapsed.
/*
* 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
/* 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,
......
/* 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>
......
......@@ -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;
}
/* This file is an image processing operation for GEGL
*
* seamless-clone-common.h
* Copyright (C) 2012 Barak Itkin <lightningismyname@gmail.com>
* 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
......@@ -17,57 +17,48 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __SEAMLESS_CLONE_COMMON_H__
#define __SEAMLESS_CLONE_COMMON_H__
#ifndef __GEGL_SC_SAMPLE_H__
#define __GEGL_SC_SAMPLE_H__
#include <poly2tri-c/p2t/poly2tri.h>
#include <poly2tri-c/refine/refine.h>
#include <poly2tri-c/render/mesh-render.h>
#include <glib.h>
#include <poly2tri/refine/refine.h>
#include "find-outline.h"
#include "make-mesh.h"
typedef enum {
SC_ERROR_NONE = 0,
SC_ERROR_NO_PASTE,
SC_ERROR_SMALL_PASTE,
SC_ERROR_HOLED_OR_SPLIT_PASTE,
} ScError;
#include "sc-outline.h"
typedef struct {
GeglRectangle bg_rect;
GeglBuffer *fg_buf;
GeglBuffer *bg_buf;
ScMeshSampling *sampling;
GHashTable *pt2col;
int xoff;
int yoff;
} ScColorComputeInfo;
/** 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 struct {
ScError error;
GeglRectangle mesh_bounds;
P2trMesh *mesh;
ScMeshSampling *sampling;
GeglBuffer *uvt;
ScOutline *outline;
} ScCache;
typedef GHashTable ScMeshSampling;
gboolean sc_render_seamless (GeglBuffer *bg,
GeglBuffer *fg,
int fg_xoff,
int fg_yoff,
GeglBuffer *dest,
const GeglRectangle *dest_rect,
const ScCache *cache);
ScCache* sc_generate_cache (GeglBuffer *fg,
const GeglRectangle *extents,
int max_refine_steps);
/**
* 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);
void sc_cache_free (ScCache *cache);
/**
* Free an ScSampleList object created by sc_sample_list_compute
*/
void sc_sample_list_free (ScSampleList *self);
#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))
/**
* 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
make-mesh.h
sc-context.h
sc-context-private.h
sc-outline.h
/* 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 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 = 4; /* 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;
}
static void
sc_point_to_color_func (P2trPoint *point,
gfloat *dest,
gpointer cci_p)
{
ScColorComputeInfo *cci = (ScColorComputeInfo*) cci_p;
ScSampleList *sl = g_hash_table_lookup (cci->sampling, point);
gint i;
gdouble weightT = 0;
guint N = sl->points->len;
const Babl *format = babl_format ("R'G'B'A float");
gfloat *col_cpy;
gfloat aux_c[4], input_c[4], dest_c[3] = {0, 0, 0};
if ((col_cpy = g_hash_table_lookup (cci->pt2col, point)) != NULL)
{
dest[0] = col_cpy[0];
dest[1] = col_cpy[1];
dest[2] = col_cpy[2];
dest[3] = col_cpy[3];
return;
}
col_cpy = g_new (gfloat, 4);
g_hash_table_insert (cci->pt2col, point, col_cpy);
for (i = 0; i < N; i++)
{
ScPoint *pt = g_ptr_array_index (sl->points, i);
gdouble weight = g_array_index (sl->weights, gdouble, i);
/* The original outline point is on (pt->x,pt->y) in terms of mesh
* coordinates. But, since we are translating, it's location in
* background coordinates is (pt->x + cci->x, pt->y + cci->y)
*/
/* 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 */
#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 (cci->bg_rect, pt->x + cci->xoff, pt->y + cci->yoff)) { continue; }
#undef sc_rect_contains
gegl_buffer_sample (cci->fg_buf, pt->x, pt->y, NULL, aux_c, format, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
/* Sample the BG with the offset */
gegl_buffer_sample (cci->bg_buf, pt->x + cci->xoff, pt->y + cci->yoff, NULL, input_c, format, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
dest_c[0] += weight * (input_c[0] - aux_c[0]);
dest_c[1] += weight * (input_c[1] - aux_c[1]);
dest_c[2] += weight * (input_c[2] - aux_c[2]);
weightT += weight;
}
col_cpy[0] = dest[0] = dest_c[0] / weightT;
col_cpy[1] = dest[1] = dest_c[1] / weightT;
col_cpy[2] = dest[2] = dest_c[2] / weightT;
col_cpy[3] = dest[3] = 1;
}
gboolean
sc_render_seamless (GeglBuffer *bg,
GeglBuffer *fg,
int fg_xoff,
int fg_yoff,
GeglBuffer *dest,
const GeglRectangle *dest_rect,
const ScCache *cache)
{
/** 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;
ScColorComputeInfo mesh_render_info;
GeglBufferIterator *iter;
int out_index, uvt_index, fg_index;
const Babl *format = babl_format("R'G'B'A float");
if (cache == NULL)
{
g_warning ("No preprocessing result given. Stop.");
return FALSE;
}
if (cache->error != SC_ERROR_NONE)
{
g_warning ("The preprocessing result contains an error. Stop.");
return FALSE;
}
if (gegl_rectangle_is_empty (&cache->mesh_bounds))