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