1 /* This file is an image processing operation for GEGL
2  *
3  * This program is free software: you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 3 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
15  *
16  * Polarize plug-in --- maps a rectangle to a circle or vice-versa
17  * Copyright (C) 1997 Daniel Dunbar
18  * Email: ddunbar@diads.com
19  * WWW:   http://millennium.diads.com/gimp/
20  * Copyright (C) 1997 Federico Mena Quintero
21  * federico@nuclecu.unam.mx
22  * Copyright (C) 1996 Marc Bless
23  * E-mail: bless@ai-lab.fh-furtwangen.de
24  * WWW:    www.ai-lab.fh-furtwangen.de/~bless
25  *
26  * Copyright (C) 2011 Robert Sasu <sasu.robert@gmail.com>
27  */
28 
29 #include "config.h"
30 #include <glib/gi18n-lib.h>
31 
32 #ifdef GEGL_PROPERTIES
33 
34 property_double (depth, _("Circle depth in percent"), 100.0)
35   value_range (0.0, 100.0)
36   ui_meta     ("unit", "percent")
37 
38 property_double (angle, _("Offset angle"), 0.0)
39   value_range   (0.0, 360.0)
40   ui_meta       ("unit", "degree")
41   ui_meta       ("direction", "ccw")
42 
43 property_boolean (bw, _("Map backwards"), FALSE)
44   description    (_("Start from the right instead of the left"))
45 
46 property_boolean (top, _("Map from top"), TRUE)
47   description    (_("Put the top row in the middle and the bottom row on the outside"))
48 
49 property_boolean (polar, _("To polar"), TRUE)
50   description    (_("Map the image to a circle"))
51 
52 property_int  (pole_x, _("X"), 0)
53   description (_("Origin point for the polar coordinates"))
54   value_range (0, G_MAXINT)
55   ui_meta     ("unit", "pixel-coordinate")
56   ui_meta     ("axis", "x")
57   ui_meta     ("sensitive", "$middle.sensitive & ! middle")
58 
59 property_int  (pole_y, _("Y"), 0)
60   description (_("Origin point for the polar coordinates"))
61   value_range (0, G_MAXINT)
62   ui_meta     ("unit", "pixel-coordinate")
63   ui_meta     ("axis", "y")
64   ui_meta     ("sensitive", "$pole-x.sensitive")
65 
66 property_boolean (middle, _("Choose middle"), TRUE)
67   description (_("Let origin point to be the middle one"))
68   ui_meta     ("sensitive", "polar")
69 
70 #else
71 
72 #define GEGL_OP_FILTER
73 #define GEGL_OP_NAME     polar_coordinates
74 #define GEGL_OP_C_SOURCE polar-coordinates.c
75 
76 #include "gegl-op.h"
77 #include <stdio.h>
78 
79 #define WITHIN(a, b, c) ((((a) <= (b)) && ((b) <= (c))) ? 1 : 0)
80 #define SQR(x) (x)*(x)
81 
82 #define SCALE_WIDTH     200
83 #define ENTRY_WIDTH      60
84 
85 static gboolean
86 calc_undistorted_coords (gdouble        wx,
87                          gdouble        wy,
88                          gdouble        cen_x,
89                          gdouble        cen_y,
90                          gdouble       *x,
91                          gdouble       *y,
92                          GeglProperties    *o,
93                          GeglRectangle  boundary)
94 {
95   gboolean inside;
96   gdouble  phi, phi2;
97   gdouble  xx, xm, ym, yy;
98   gint     xdiff, ydiff;
99   gdouble  r;
100   gdouble  m;
101   gdouble  xmax, ymax, rmax;
102   gdouble  x_calc, y_calc;
103   gdouble  xi, yi;
104   gdouble  circle, angl, t, angle;
105   gint     x1, x2, y1, y2;
106 
107   /* initialize */
108 
109   phi = 0.0;
110   r   = 0.0;
111 
112   x1     = 0;
113   y1     = 0;
114   x2     = boundary.width;
115   y2     = boundary.height;
116   xdiff  = x2 - x1;
117   ydiff  = y2 - y1;
118   xm     = xdiff / 2.0;
119   ym     = ydiff / 2.0;
120   circle = o->depth;
121   angle  = o->angle;
122   angl   = (gdouble) angle / 180.0 * G_PI;
123 
124   if (o->polar)
125     {
126       if (wx >= cen_x)
127         {
128           if (wy > cen_y)
129             {
130               phi = G_PI - atan (((double)(wx - cen_x))/
131                                  ((double)(wy - cen_y)));
132             }
133           else if (wy < cen_y)
134             {
135               phi = atan (((double)(wx - cen_x))/((double)(cen_y - wy)));
136             }
137           else
138             {
139               phi = G_PI / 2;
140             }
141         }
142       else if (wx < cen_x)
143         {
144           if (wy < cen_y)
145             {
146               phi = 2 * G_PI - atan (((double)(cen_x -wx)) /
147                                      ((double)(cen_y - wy)));
148             }
149           else if (wy > cen_y)
150             {
151               phi = G_PI + atan (((double)(cen_x - wx))/
152                                  ((double)(wy - cen_y)));
153             }
154           else
155             {
156               phi = 1.5 * G_PI;
157             }
158         }
159 
160       r   = sqrt (SQR (wx - cen_x) + SQR (wy - cen_y));
161 
162       if (wx != cen_x)
163         {
164           m = fabs (((double)(wy - cen_y)) / ((double)(wx - cen_x)));
165         }
166       else
167         {
168           m = 0;
169         }
170 
171       if (m <= ((double)(y2 - y1) / (double)(x2 - x1)))
172         {
173           if (wx == cen_x)
174             {
175               xmax = 0;
176               ymax = cen_y - y1;
177             }
178           else
179             {
180               xmax = cen_x - x1;
181               ymax = m * xmax;
182             }
183         }
184       else
185         {
186           ymax = cen_y - y1;
187           xmax = ymax / m;
188         }
189 
190       rmax = sqrt ( (double)(SQR (xmax) + SQR (ymax)) );
191 
192       t = ((cen_y - y1) < (cen_x - x1)) ? (cen_y - y1) : (cen_x - x1);
193       rmax = (rmax - t) / 100 * (100 - circle) + t;
194 
195       phi = fmod (phi + angl, 2*G_PI);
196 
197       if (o->bw)
198         x_calc = x2 - 1 - (x2 - x1 - 1)/(2*G_PI) * phi;
199       else
200         x_calc = (x2 - x1 - 1)/(2*G_PI) * phi + x1;
201 
202       if (o->top)
203         y_calc = (y2 - y1)/rmax   * r   + y1;
204       else
205         y_calc = y2 - (y2 - y1)/rmax * r;
206     }
207   else
208     {
209       if (o->bw)
210         phi = (2 * G_PI) * (x2 - wx) / xdiff;
211       else
212         phi = (2 * G_PI) * (wx - x1) / xdiff;
213 
214       phi = fmod (phi + angl, 2 * G_PI);
215 
216       if (phi >= 1.5 * G_PI)
217         phi2 = 2 * G_PI - phi;
218       else if (phi >= G_PI)
219         phi2 = phi - G_PI;
220       else if (phi >= 0.5 * G_PI)
221         phi2 = G_PI - phi;
222       else
223         phi2 = phi;
224 
225       xx = tan (phi2);
226       if (xx != 0)
227         m = (double) 1.0 / xx;
228       else
229         m = 0;
230 
231       if (m <= ((double)(ydiff) / (double)(xdiff)))
232         {
233           if (phi2 == 0)
234             {
235               xmax = 0;
236               ymax = ym - y1;
237             }
238           else
239             {
240               xmax = xm - x1;
241               ymax = m * xmax;
242             }
243         }
244       else
245         {
246           ymax = ym - y1;
247           xmax = ymax / m;
248         }
249 
250       rmax = sqrt ((double)(SQR (xmax) + SQR (ymax)));
251 
252       t = ((ym - y1) < (xm - x1)) ? (ym - y1) : (xm - x1);
253 
254       rmax = (rmax - t) / 100.0 * (100 - circle) + t;
255 
256       if (o->top)
257         r = rmax * (double)((wy - y1) / (double)(ydiff));
258       else
259         r = rmax * (double)((y2 - wy) / (double)(ydiff));
260 
261       xx = r * sin (phi2);
262       yy = r * cos (phi2);
263 
264       if (phi >= 1.5 * G_PI)
265         {
266           x_calc = (double)xm - xx;
267           y_calc = (double)ym - yy;
268         }
269       else if (phi >= G_PI)
270         {
271           x_calc = (double)xm - xx;
272           y_calc = (double)ym + yy;
273         }
274       else if (phi >= 0.5 * G_PI)
275         {
276           x_calc = (double)xm + xx;
277           y_calc = (double)ym + yy;
278         }
279       else
280         {
281           x_calc = (double)xm + xx;
282           y_calc = (double)ym - yy;
283         }
284     }
285 
286   xi = (int) (x_calc + 0.5);
287   yi = (int) (y_calc + 0.5);
288 
289   inside = (WITHIN (0, xi, boundary.width - 1) && WITHIN (0, yi, boundary.height - 1));
290   if (inside)
291     {
292       *x = x_calc;
293       *y = y_calc;
294     }
295   return inside;
296 }
297 
298 
299 static GeglRectangle
300 get_effective_area (GeglOperation *operation)
301 {
302   GeglRectangle  result = {0,0,0,0};
303   GeglRectangle *in_rect = gegl_operation_source_get_bounding_box (operation, "input");
304 
305   gegl_rectangle_copy(&result, in_rect);
306 
307   return result;
308 }
309 
310 static gboolean
311 process (GeglOperation       *operation,
312          GeglBuffer          *input,
313          GeglBuffer          *output,
314          const GeglRectangle *result,
315          gint                 level)
316 {
317   GeglProperties          *o            = GEGL_PROPERTIES (operation);
318   GeglRectangle            boundary     = get_effective_area (operation);
319   const Babl              *format       = gegl_operation_get_format (operation, "output");
320   GeglSampler             *sampler      = gegl_buffer_sampler_new_at_level (
321                                     input, format, GEGL_SAMPLER_NOHALO,
322                                     level);
323 
324   gint      x,y;
325   gfloat   *src_buf, *dst_buf;
326   gfloat    dest[4];
327   gint      i, offset = 0;
328   gboolean  inside;
329   gdouble   px, py;
330   gdouble   cen_x, cen_y;
331 
332   GeglBufferMatrix2  scale;  /* a matrix indicating scaling factors around the
333                                 current center pixel.
334                              */
335 
336   src_buf = g_new0 (gfloat, result->width * result->height * 4);
337   dst_buf = g_new0 (gfloat, result->width * result->height * 4);
338 
339   gegl_buffer_get (input, result, 1.0, format, src_buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
340 
341   if (o->middle)
342     {
343       cen_x = boundary.width / 2;
344       cen_y = boundary.height / 2;
345     }
346   else
347     {
348       cen_x = o->pole_x;
349       cen_y = o->pole_y;
350     }
351 
352   for (y = result->y; y < result->y + result->height; y++)
353     for (x = result->x; x < result->x + result->width; x++)
354       {
355 #define gegl_unmap(u,v,ud,vd) {                                         \
356           gdouble rx = 0.0, ry = 0.0;                                   \
357           inside = calc_undistorted_coords ((gdouble)x, (gdouble)y,     \
358                                             cen_x, cen_y, &rx, &ry,     \
359                                             o, boundary);               \
360           ud = rx;                                                      \
361           vd = ry;                                                      \
362         }
363         gegl_sampler_compute_scale (scale, x, y);
364         gegl_unmap(x,y,px,py);
365 #undef gegl_unmap
366 
367         if (inside)
368           gegl_sampler_get (sampler, px, py, &scale, dest,
369                             GEGL_ABYSS_NONE);
370         else
371           for (i=0; i<4; i++)
372             dest[i] = 0.0;
373 
374         for (i=0; i<4; i++)
375           dst_buf[offset++] = dest[i];
376       }
377 
378   gegl_buffer_set (output, result, 0, format, dst_buf, GEGL_AUTO_ROWSTRIDE);
379 
380   g_free (src_buf);
381   g_free (dst_buf);
382 
383   g_object_unref (sampler);
384 
385   return  TRUE;
386 }
387 
388 static GeglRectangle
389 get_required_for_output (GeglOperation       *operation,
390                          const gchar         *input_pad,
391                          const GeglRectangle *roi)
392 {
393   const GeglRectangle *in_rect =
394             gegl_operation_source_get_bounding_box (operation, "input");
395 
396   if (! in_rect || gegl_rectangle_is_infinite_plane (in_rect))
397     {
398       return *roi;
399     }
400 
401   return *in_rect;
402 }
403 
404 static gboolean
405 operation_process (GeglOperation        *operation,
406                    GeglOperationContext *context,
407                    const gchar          *output_prop,
408                    const GeglRectangle  *result,
409                    gint                  level)
410 {
411   GeglOperationClass  *operation_class;
412 
413   const GeglRectangle *in_rect =
414     gegl_operation_source_get_bounding_box (operation, "input");
415 
416   if (in_rect && gegl_rectangle_is_infinite_plane (in_rect))
417     {
418       gpointer in = gegl_operation_context_get_object (context, "input");
419       gegl_operation_context_take_object (context, "output",
420                                           g_object_ref (G_OBJECT (in)));
421       return TRUE;
422     }
423 
424   operation_class = GEGL_OPERATION_CLASS (gegl_op_parent_class);
425 
426   return operation_class->process (operation, context, output_prop, result,
427                                    gegl_operation_context_get_level (context));
428 }
429 
430 static void
431 gegl_op_class_init (GeglOpClass *klass)
432 {
433   GeglOperationClass       *operation_class;
434   GeglOperationFilterClass *filter_class;
435 
436   operation_class = GEGL_OPERATION_CLASS (klass);
437   filter_class    = GEGL_OPERATION_FILTER_CLASS (klass);
438 
439   operation_class->get_required_for_output = get_required_for_output;
440   operation_class->process                 = operation_process;
441 
442   filter_class->process                    = process;
443 
444   gegl_operation_class_set_keys (operation_class,
445     "name",               "gegl:polar-coordinates",
446     "title",              _("Polar Coordinates"),
447     "categories",         "transform:map",
448     "position-dependent", "true",
449     "reference-hash",     "4716987c6105311bd29937d5d427f59b",
450     "license",            "GPL3+",
451     "description", _("Convert image to or from polar coordinates"),
452     NULL);
453 }
454 
455 #endif
456