1 /* This file is an image processing operation for GEGL
3 * GEGL is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 3 of the License, or (at your option) any later version.
8 * GEGL 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 GNU
11 * Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
16 * Copyright (C) 2017 Ell
20 #include <glib/gi18n-lib.h>
22 #ifdef GEGL_PROPERTIES
24 enum_start (gegl_spherize_mode
)
25 enum_value (GEGL_SPHERIZE_MODE_RADIAL
, "radial", N_("Radial"))
26 enum_value (GEGL_SPHERIZE_MODE_HORIZONTAL
, "horizontal", N_("Horizontal"))
27 enum_value (GEGL_SPHERIZE_MODE_VERTICAL
, "vertical", N_("Vertical"))
28 enum_end (GeglSpherizeMode
)
30 property_enum (mode
, _("Mode"),
31 GeglSpherizeMode
, gegl_spherize_mode
,
32 GEGL_SPHERIZE_MODE_RADIAL
)
33 description (_("Displacement mode"))
35 property_double (angle_of_view
, _("Angle of view"), 0.0)
36 description (_("Camera angle of view"))
37 value_range (0.0, 180.0)
38 ui_meta ("unit", "degree")
40 property_double (curvature
, _("Curvature"), 1.0)
41 description (_("Spherical cap apex angle, as a fraction of the co-angle of view"))
42 value_range (0.0, 1.0) /* note that the code can handle negative curvatures
43 * (in the [-1, 0) range), in which case the image is
44 * wrapped around the back face, rather than the front
45 * face, of the spherical cap. we disable negative
46 * curvatures atm, in particular, since they produce
47 * the same result when the angle of view is 0, and
48 * since their upper-bound, wrt the angle of view, is
52 property_double (amount
, _("Amount"), 1.0)
53 description (_("Displacement scaling factor (negative values refer to the inverse displacement)"))
54 value_range (-1.0, 1.0)
56 property_enum (sampler_type
, _("Resampling method"),
57 GeglSamplerType
, gegl_sampler_type
, GEGL_SAMPLER_LINEAR
)
58 description (_("Mathematical method for reconstructing pixel values"))
62 #define GEGL_OP_FILTER
63 #define GEGL_OP_NAME spherize
64 #define GEGL_OP_C_SOURCE spherize.c
71 is_nop (GeglOperation
*operation
)
73 GeglProperties
*o
= GEGL_PROPERTIES (operation
);
74 GeglRectangle
*in_rect
;
76 if (fabs (o
->curvature
) < EPSILON
|| fabs (o
->amount
) < EPSILON
)
79 in_rect
= gegl_operation_source_get_bounding_box (operation
, "input");
81 if (in_rect
&& gegl_rectangle_is_infinite_plane (in_rect
))
86 case GEGL_SPHERIZE_MODE_RADIAL
:
87 return in_rect
->width
< 1 || in_rect
->height
< 1;
89 case GEGL_SPHERIZE_MODE_HORIZONTAL
:
90 return in_rect
->width
< 1;
92 case GEGL_SPHERIZE_MODE_VERTICAL
:
93 return in_rect
->height
< 1;
96 g_return_val_if_reached (TRUE
);
100 get_required_for_output (GeglOperation
*operation
,
101 const gchar
*input_pad
,
102 const GeglRectangle
*roi
)
104 GeglRectangle result
= *roi
;
106 if (! is_nop (operation
))
108 GeglProperties
*o
= GEGL_PROPERTIES (operation
);
109 GeglRectangle
*in_rect
= gegl_operation_source_get_bounding_box (operation
, "input");
115 case GEGL_SPHERIZE_MODE_RADIAL
:
119 case GEGL_SPHERIZE_MODE_HORIZONTAL
:
120 result
.x
= in_rect
->x
;
121 result
.width
= in_rect
->width
;
124 case GEGL_SPHERIZE_MODE_VERTICAL
:
125 result
.y
= in_rect
->y
;
126 result
.height
= in_rect
->height
;
136 parent_process (GeglOperation
*operation
,
137 GeglOperationContext
*context
,
138 const gchar
*output_prop
,
139 const GeglRectangle
*result
,
142 if (is_nop (operation
))
146 input
= gegl_operation_context_get_object (context
, "input");
148 gegl_operation_context_set_object (context
, "output", input
);
153 return GEGL_OPERATION_CLASS (gegl_op_parent_class
)->process (operation
,
160 process (GeglOperation
*operation
,
163 const GeglRectangle
*roi
,
166 GeglProperties
*o
= GEGL_PROPERTIES (operation
);
167 const Babl
*format
= gegl_operation_get_format (operation
, "output");
168 GeglSampler
*sampler
;
169 GeglBufferIterator
*iter
;
170 const GeglRectangle
*in_extent
;
172 gdouble dx
= 0.0, dy
= 0.0;
173 gdouble coangle_of_view_2
;
174 gdouble focal_length
;
175 gdouble curvature_sign
;
180 gdouble f
, f2
, r
, r_inv
, r2
, p
, f_p
, f_p2
, f_pf
, a
, a_inv
, sgn
;
181 gboolean perspective
;
185 sampler
= gegl_buffer_sampler_new_at_level (input
, format
,
186 o
->sampler_type
, level
);
188 iter
= gegl_buffer_iterator_new (output
, roi
, level
, format
,
189 GEGL_ACCESS_WRITE
, GEGL_ABYSS_NONE
, 2);
191 gegl_buffer_iterator_add (iter
, input
, roi
, level
, format
,
192 GEGL_ACCESS_READ
, GEGL_ABYSS_NONE
);
194 in_extent
= gegl_operation_source_get_bounding_box (operation
, "input");
196 cx
= in_extent
->x
+ in_extent
->width
/ 2.0;
197 cy
= in_extent
->y
+ in_extent
->height
/ 2.0;
199 if (o
->mode
== GEGL_SPHERIZE_MODE_RADIAL
||
200 o
->mode
== GEGL_SPHERIZE_MODE_HORIZONTAL
)
202 dx
= 2.0 / (in_extent
->width
- 1);
204 if (o
->mode
== GEGL_SPHERIZE_MODE_RADIAL
||
205 o
->mode
== GEGL_SPHERIZE_MODE_VERTICAL
)
207 dy
= 2.0 / (in_extent
->height
- 1);
210 coangle_of_view_2
= MAX (180.0 - o
->angle_of_view
, 0.01) * G_PI
/ 360.0;
211 focal_length
= tan (coangle_of_view_2
);
212 curvature_sign
= o
->curvature
> 0.0 ? +1.0 : -1.0;
213 cap_angle_2
= fabs (o
->curvature
) * coangle_of_view_2
;
214 cap_radius
= 1.0 / sin (cap_angle_2
);
215 cap_depth
= curvature_sign
* cap_radius
* cos (cap_angle_2
);
216 factor
= fabs (o
->amount
);
229 sgn
= curvature_sign
;
231 perspective
= o
->angle_of_view
> EPSILON
;
232 inverse
= o
->amount
< 0.0;
234 while (gegl_buffer_iterator_next (iter
))
236 gfloat
*out_pixel
= iter
->items
[0].data
;
237 const gfloat
*in_pixel
= iter
->items
[1].data
;
238 GeglRectangle
*roi
= &iter
->items
[0].roi
;
241 y
= dy
* (roi
->y
+ 0.5 - cy
);
243 for (j
= roi
->y
; j
< roi
->y
+ roi
->height
; j
++, y
+= dy
)
245 x
= dx
* (roi
->x
+ 0.5 - cx
);
247 for (i
= roi
->x
; i
< roi
->x
+ roi
->width
; i
++, x
+= dx
)
253 if (d2
> EPSILON
&& d2
< 1.0 - EPSILON
)
255 gdouble d
= sqrt (d2
);
257 gdouble src_x
, src_y
;
261 gdouble d2_f2
= d2
+ f2
;
264 src_d
= (f_pf
- sgn
* sqrt (d2_f2
* r2
- f_p2
* d2
)) * d
/ d2_f2
;
266 src_d
= (G_PI_2
- acos (src_d
* r_inv
)) * a_inv
;
270 src_d
= r
* cos (G_PI_2
- src_d
* a
);
273 src_d
= f
* src_d
/ (f_p
- sgn
* sqrt (r2
- src_d
* src_d
));
277 src_d
= d
+ (src_d
- d
) * factor
;
279 src_x
= dx
? cx
+ src_d
* x
/ (dx
* d
) :
281 src_y
= dy
? cy
+ src_d
* y
/ (dy
* d
) :
284 gegl_sampler_get (sampler
, src_x
, src_y
,
285 NULL
, out_pixel
, GEGL_ABYSS_NONE
);
289 memcpy (out_pixel
, in_pixel
, sizeof (gfloat
) * 4);
298 g_object_unref (sampler
);
304 gegl_op_class_init (GeglOpClass
*klass
)
306 GeglOperationClass
*operation_class
;
307 GeglOperationFilterClass
*filter_class
;
309 operation_class
= GEGL_OPERATION_CLASS (klass
);
310 filter_class
= GEGL_OPERATION_FILTER_CLASS (klass
);
312 operation_class
->get_invalidated_by_change
= get_required_for_output
;
313 operation_class
->get_required_for_output
= get_required_for_output
;
314 operation_class
->process
= parent_process
;
316 filter_class
->process
= process
;
318 gegl_operation_class_set_keys (operation_class
,
319 "name", "gegl:spherize",
320 "title", _("Spherize"),
321 "categories", "distort:map",
322 "position-dependent", "true",
323 "reference-hash", "215f04f9ad3e27325dfbe834963a6f49",
324 "description", _("Wrap image around a spherical cap"),