1 /* GIMP - The GNU Image Manipulation Program
2  *
3  * gimpoperationcagecoefcalc.c
4  * Copyright (C) 2010 Michael Muré <batolettre@gmail.com>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 #include "config.h"
21 
22 #include <gdk-pixbuf/gdk-pixbuf.h>
23 #include <gegl.h>
24 
25 #include "libgimpmath/gimpmath.h"
26 
27 #include "operations-types.h"
28 
29 #include "gimpoperationcagecoefcalc.h"
30 #include "gimpcageconfig.h"
31 
32 #include "gimp-intl.h"
33 
34 
35 static void           gimp_operation_cage_coef_calc_finalize         (GObject              *object);
36 static void           gimp_operation_cage_coef_calc_get_property     (GObject              *object,
37                                                                       guint                 property_id,
38                                                                       GValue               *value,
39                                                                       GParamSpec           *pspec);
40 static void           gimp_operation_cage_coef_calc_set_property     (GObject              *object,
41                                                                       guint                 property_id,
42                                                                       const GValue         *value,
43                                                                       GParamSpec           *pspec);
44 
45 static void           gimp_operation_cage_coef_calc_prepare          (GeglOperation        *operation);
46 static GeglRectangle  gimp_operation_cage_coef_calc_get_bounding_box (GeglOperation        *operation);
47 static gboolean       gimp_operation_cage_coef_calc_process          (GeglOperation        *operation,
48                                                                       GeglBuffer           *output,
49                                                                       const GeglRectangle  *roi,
50                                                                       gint                  level);
51 
52 
G_DEFINE_TYPE(GimpOperationCageCoefCalc,gimp_operation_cage_coef_calc,GEGL_TYPE_OPERATION_SOURCE)53 G_DEFINE_TYPE (GimpOperationCageCoefCalc, gimp_operation_cage_coef_calc,
54                GEGL_TYPE_OPERATION_SOURCE)
55 
56 #define parent_class gimp_operation_cage_coef_calc_parent_class
57 
58 
59 static void
60 gimp_operation_cage_coef_calc_class_init (GimpOperationCageCoefCalcClass *klass)
61 {
62   GObjectClass             *object_class    = G_OBJECT_CLASS (klass);
63   GeglOperationSourceClass *source_class    = GEGL_OPERATION_SOURCE_CLASS (klass);
64   GeglOperationClass       *operation_class = GEGL_OPERATION_CLASS (klass);
65 
66   gegl_operation_class_set_keys (operation_class,
67                                  "name",        "gimp:cage-coef-calc",
68                                  "categories",  "transform",
69                                  "description", _("Compute a set of coefficient buffer for the GIMP cage tool"),
70                                  NULL);
71 
72   operation_class->prepare            = gimp_operation_cage_coef_calc_prepare;
73   operation_class->get_bounding_box   = gimp_operation_cage_coef_calc_get_bounding_box;
74   operation_class->cache_policy       = GEGL_CACHE_POLICY_ALWAYS;
75   operation_class->get_cached_region  = NULL;
76 
77   source_class->process               = gimp_operation_cage_coef_calc_process;
78 
79   object_class->get_property          = gimp_operation_cage_coef_calc_get_property;
80   object_class->set_property          = gimp_operation_cage_coef_calc_set_property;
81   object_class->finalize              = gimp_operation_cage_coef_calc_finalize;
82 
83   g_object_class_install_property (object_class,
84                                    GIMP_OPERATION_CAGE_COEF_CALC_PROP_CONFIG,
85                                    g_param_spec_object ("config",
86                                                         "Config",
87                                                         "A GimpCageConfig object, that define the transformation",
88                                                         GIMP_TYPE_CAGE_CONFIG,
89                                                         G_PARAM_READWRITE |
90                                                         G_PARAM_CONSTRUCT));
91 }
92 
93 static void
gimp_operation_cage_coef_calc_init(GimpOperationCageCoefCalc * self)94 gimp_operation_cage_coef_calc_init (GimpOperationCageCoefCalc *self)
95 {
96 }
97 
98 static void
gimp_operation_cage_coef_calc_finalize(GObject * object)99 gimp_operation_cage_coef_calc_finalize (GObject *object)
100 {
101   GimpOperationCageCoefCalc *self = GIMP_OPERATION_CAGE_COEF_CALC (object);
102 
103   g_clear_object (&self->config);
104 
105   G_OBJECT_CLASS (parent_class)->finalize (object);
106 }
107 
108 static void
gimp_operation_cage_coef_calc_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)109 gimp_operation_cage_coef_calc_get_property (GObject    *object,
110                                             guint       property_id,
111                                             GValue     *value,
112                                             GParamSpec *pspec)
113 {
114   GimpOperationCageCoefCalc *self = GIMP_OPERATION_CAGE_COEF_CALC (object);
115 
116   switch (property_id)
117     {
118     case GIMP_OPERATION_CAGE_COEF_CALC_PROP_CONFIG:
119       g_value_set_object (value, self->config);
120       break;
121 
122     default:
123       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
124       break;
125     }
126 }
127 
128 static void
gimp_operation_cage_coef_calc_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)129 gimp_operation_cage_coef_calc_set_property (GObject      *object,
130                                             guint         property_id,
131                                             const GValue *value,
132                                             GParamSpec   *pspec)
133 {
134   GimpOperationCageCoefCalc *self = GIMP_OPERATION_CAGE_COEF_CALC (object);
135 
136   switch (property_id)
137     {
138     case GIMP_OPERATION_CAGE_COEF_CALC_PROP_CONFIG:
139       if (self->config)
140         g_object_unref (self->config);
141       self->config = g_value_dup_object (value);
142       break;
143 
144    default:
145       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
146       break;
147     }
148 }
149 
150 static gboolean
gimp_operation_cage_coef_calc_is_on_straight(GimpVector2 * d1,GimpVector2 * d2,GimpVector2 * p)151 gimp_operation_cage_coef_calc_is_on_straight (GimpVector2 *d1,
152                                               GimpVector2 *d2,
153                                               GimpVector2 *p)
154 {
155   GimpVector2 v1, v2;
156   gfloat      deter;
157 
158   v1.x = p->x - d1->x;
159   v1.y = p->y - d1->y;
160   v2.x = d2->x - d1->x;
161   v2.y = d2->y - d1->y;
162 
163   gimp_vector2_normalize (&v1);
164   gimp_vector2_normalize (&v2);
165 
166   deter = v1.x * v2.y - v2.x * v1.y;
167 
168   return (deter < 0.000000001) && (deter > -0.000000001);
169 }
170 
171 static void
gimp_operation_cage_coef_calc_prepare(GeglOperation * operation)172 gimp_operation_cage_coef_calc_prepare (GeglOperation *operation)
173 {
174   GimpOperationCageCoefCalc *occc   = GIMP_OPERATION_CAGE_COEF_CALC (operation);
175   GimpCageConfig            *config = GIMP_CAGE_CONFIG (occc->config);
176 
177   gegl_operation_set_format (operation,
178                              "output",
179                              babl_format_n (babl_type ("float"),
180                                             2 * gimp_cage_config_get_n_points (config)));
181 }
182 
183 static GeglRectangle
gimp_operation_cage_coef_calc_get_bounding_box(GeglOperation * operation)184 gimp_operation_cage_coef_calc_get_bounding_box (GeglOperation *operation)
185 {
186   GimpOperationCageCoefCalc *occc   = GIMP_OPERATION_CAGE_COEF_CALC (operation);
187   GimpCageConfig            *config = GIMP_CAGE_CONFIG (occc->config);
188 
189   return gimp_cage_config_get_bounding_box (config);
190 }
191 
192 static gboolean
gimp_operation_cage_coef_calc_process(GeglOperation * operation,GeglBuffer * output,const GeglRectangle * roi,gint level)193 gimp_operation_cage_coef_calc_process (GeglOperation       *operation,
194                                        GeglBuffer          *output,
195                                        const GeglRectangle *roi,
196                                        gint                 level)
197 {
198   GimpOperationCageCoefCalc *occc   = GIMP_OPERATION_CAGE_COEF_CALC (operation);
199   GimpCageConfig            *config = GIMP_CAGE_CONFIG (occc->config);
200 
201   const Babl *format;
202 
203   GeglBufferIterator *it;
204   guint               n_cage_vertices;
205   GimpCagePoint      *current, *last;
206 
207   if (! config)
208     return FALSE;
209 
210   format = babl_format_n (babl_type ("float"), 2 * gimp_cage_config_get_n_points (config));
211 
212   n_cage_vertices   = gimp_cage_config_get_n_points (config);
213 
214   it = gegl_buffer_iterator_new (output, roi, 0, format,
215                                  GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 1);
216 
217   while (gegl_buffer_iterator_next (it))
218     {
219       /* iterate inside the roi */
220       gfloat *coef     = it->items[0].data;
221       gint    n_pixels = it->length;
222       gint    x        = it->items[0].roi.x; /* initial x         */
223       gint    y        = it->items[0].roi.y; /* and y coordinates */
224       gint    j;
225 
226       memset (coef, 0, sizeof * coef * n_pixels * 2 * n_cage_vertices);
227       while(n_pixels--)
228         {
229           if (gimp_cage_config_point_inside(config, x, y))
230             {
231               last = &(g_array_index (config->cage_points, GimpCagePoint, 0));
232 
233               for( j = 0; j < n_cage_vertices; j++)
234                 {
235                   GimpVector2 v1,v2,a,b,p;
236                   gdouble BA,SRT,L0,L1,A0,A1,A10,L10, Q,S,R, absa;
237 
238                   current = &(g_array_index (config->cage_points, GimpCagePoint, (j+1) % n_cage_vertices));
239                   v1 = last->src_point;
240                   v2 = current->src_point;
241                   p.x = x;
242                   p.y = y;
243                   a.x = v2.x - v1.x;
244                   a.y = v2.y - v1.y;
245                   absa = gimp_vector2_length (&a);
246 
247                   b.x = v1.x - x;
248                   b.y = v1.y - y;
249                   Q = a.x * a.x + a.y * a.y;
250                   S = b.x * b.x + b.y * b.y;
251                   R = 2.0 * (a.x * b.x + a.y * b.y);
252                   BA = b.x * a.y - b.y * a.x;
253                   SRT = sqrt(4.0 * S * Q - R * R);
254 
255                   L0 = log(S);
256                   L1 = log(S + Q + R);
257                   A0 = atan2(R, SRT) / SRT;
258                   A1 = atan2(2.0 * Q + R, SRT) / SRT;
259                   A10 = A1 - A0;
260                   L10 = L1 - L0;
261 
262                   /* edge coef */
263                   coef[j + n_cage_vertices] = (-absa / (4.0 * G_PI)) * ((4.0*S-(R*R)/Q) * A10 + (R / (2.0 * Q)) * L10 + L1 - 2.0);
264 
265                   if (isnan(coef[j + n_cage_vertices]))
266                     {
267                       coef[j + n_cage_vertices] = 0.0;
268                     }
269 
270                   /* vertice coef */
271                   if (!gimp_operation_cage_coef_calc_is_on_straight (&v1, &v2, &p))
272                     {
273                       coef[j] += (BA / (2.0 * G_PI)) * (L10 /(2.0*Q) - A10 * (2.0 + R / Q));
274                       coef[(j+1)%n_cage_vertices] -= (BA / (2.0 * G_PI)) * (L10 / (2.0 * Q) - A10 * (R / Q));
275                     }
276 
277                   last = current;
278                 }
279             }
280 
281           coef += 2 * n_cage_vertices;
282 
283           /* update x and y coordinates */
284           x++;
285           if (x >= (it->items[0].roi.x + it->items[0].roi.width))
286             {
287               x = it->items[0].roi.x;
288               y++;
289             }
290         }
291     }
292 
293   return TRUE;
294 }
295