Update Chinese (China) translation
[gegl.git] / operations / common / spherize.c
blobd62983a62d422cfbedbd04d516cad406f07a201c
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
19 #include "config.h"
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
49 * arbitrary.
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"))
60 #else
62 #define GEGL_OP_FILTER
63 #define GEGL_OP_NAME spherize
64 #define GEGL_OP_C_SOURCE spherize.c
66 #include "gegl-op.h"
68 #define EPSILON 1e-10
70 static gboolean
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)
77 return TRUE;
79 in_rect = gegl_operation_source_get_bounding_box (operation, "input");
81 if (in_rect && gegl_rectangle_is_infinite_plane (in_rect))
82 return TRUE;
84 switch (o->mode)
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);
99 static GeglRectangle
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");
111 if (in_rect)
113 switch (o->mode)
115 case GEGL_SPHERIZE_MODE_RADIAL:
116 result = *in_rect;
117 break;
119 case GEGL_SPHERIZE_MODE_HORIZONTAL:
120 result.x = in_rect->x;
121 result.width = in_rect->width;
122 break;
124 case GEGL_SPHERIZE_MODE_VERTICAL:
125 result.y = in_rect->y;
126 result.height = in_rect->height;
127 break;
132 return result;
135 static gboolean
136 parent_process (GeglOperation *operation,
137 GeglOperationContext *context,
138 const gchar *output_prop,
139 const GeglRectangle *result,
140 gint level)
142 if (is_nop (operation))
144 GObject *input;
146 input = gegl_operation_context_get_object (context, "input");
148 gegl_operation_context_set_object (context, "output", input);
150 return TRUE;
153 return GEGL_OPERATION_CLASS (gegl_op_parent_class)->process (operation,
154 context,
155 output_prop,
156 result, level);
159 static gboolean
160 process (GeglOperation *operation,
161 GeglBuffer *input,
162 GeglBuffer *output,
163 const GeglRectangle *roi,
164 gint level)
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;
171 gdouble cx, cy;
172 gdouble dx = 0.0, dy = 0.0;
173 gdouble coangle_of_view_2;
174 gdouble focal_length;
175 gdouble curvature_sign;
176 gdouble cap_angle_2;
177 gdouble cap_radius;
178 gdouble cap_depth;
179 gdouble factor;
180 gdouble f, f2, r, r_inv, r2, p, f_p, f_p2, f_pf, a, a_inv, sgn;
181 gboolean perspective;
182 gboolean inverse;
183 gint i, j;
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);
218 f = focal_length;
219 f2 = f * f;
220 r = cap_radius;
221 r_inv = 1 / r;
222 r2 = r * r;
223 p = cap_depth;
224 f_p = f + p;
225 f_p2 = f_p * f_p;
226 f_pf = f_p * f;
227 a = cap_angle_2;
228 a_inv = 1 / a;
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;
239 gfloat x, y;
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)
249 gfloat d2;
251 d2 = x * x + y * y;
253 if (d2 > EPSILON && d2 < 1.0 - EPSILON)
255 gdouble d = sqrt (d2);
256 gdouble src_d = d;
257 gdouble src_x, src_y;
259 if (! inverse)
261 gdouble d2_f2 = d2 + f2;
263 if (perspective)
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;
268 else
270 src_d = r * cos (G_PI_2 - src_d * a);
272 if (perspective)
273 src_d = f * src_d / (f_p - sgn * sqrt (r2 - src_d * src_d));
276 if (factor < 1.0)
277 src_d = d + (src_d - d) * factor;
279 src_x = dx ? cx + src_d * x / (dx * d) :
280 i + 0.5;
281 src_y = dy ? cy + src_d * y / (dy * d) :
282 j + 0.5;
284 gegl_sampler_get (sampler, src_x, src_y,
285 NULL, out_pixel, GEGL_ABYSS_NONE);
287 else
289 memcpy (out_pixel, in_pixel, sizeof (gfloat) * 4);
292 out_pixel += 4;
293 in_pixel += 4;
298 g_object_unref (sampler);
300 return TRUE;
303 static void
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"),
325 NULL);
328 #endif