"Fossies" - the Fresh Open Source Software Archive

Member "darktable-2.6.3/src/develop/masks/ellipse.c" (20 Oct 2019, 61346 Bytes) of package /linux/misc/darktable-2.6.3.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ellipse.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.6.2_vs_2.6.3.

    1 /*
    2     This file is part of darktable,
    3     copyright (c) 2012--2013 aldric renaudin.
    4     copyright (c) 2013--2016 Ulrich Pegelow.
    5 
    6     darktable 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     darktable 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 darktable.  If not, see <http://www.gnu.org/licenses/>.
   18 */
   19 #include "common/debug.h"
   20 #include "control/conf.h"
   21 #include "control/control.h"
   22 #include "develop/blend.h"
   23 #include "develop/imageop.h"
   24 #include "develop/masks.h"
   25 
   26 static inline void _ellipse_point_transform(const float xref, const float yref, const float x, const float y,
   27                                             const float sinr, const float cosr, const float scalea,
   28                                             const float scaleb, const float sinv, const float cosv,
   29                                             float *xnew, float *ynew)
   30 {
   31   float xtmp = (scaleb * sinr * sinr + scalea * cosr * cosr) * (x - xref)
   32                + (scalea * cosr * sinr - scaleb * cosr * sinr) * (y - yref);
   33   float ytmp = (scalea * cosr * sinr - scaleb * cosr * sinr) * (x - xref)
   34                + (scalea * sinr * sinr + scaleb * cosr * cosr) * (y - yref);
   35 
   36   *xnew = xref + cosv * xtmp - sinv * ytmp;
   37   *ynew = yref + sinv * xtmp + cosv * ytmp;
   38 }
   39 
   40 // Jordan's point in polygon test
   41 static int dt_ellipse_cross_test(float x, float y, float *point_1, float *point_2)
   42 {
   43   float x_a = x;
   44   float y_a = y;
   45   float x_b = point_1[0];
   46   float y_b = point_1[1];
   47   float x_c = point_2[0];
   48   float y_c = point_2[1];
   49 
   50   if(y_a == y_b && y_b == y_c)
   51   {
   52     if((x_b <= x_a && x_a <= x_c) || (x_c <= x_a && x_a <= x_b))
   53       return 0;
   54     else
   55       return 1;
   56   }
   57 
   58   if(y_b > y_c)
   59   {
   60     float tmp;
   61     tmp = x_b, x_b = x_c, x_c = tmp;
   62     tmp = y_b, y_b = y_c, y_c = tmp;
   63   }
   64 
   65   if(y_a == y_b && x_a == x_b) return 0;
   66 
   67   if(y_a <= y_b || y_a > y_c) return 1;
   68 
   69   float delta = (x_b - x_a) * (y_c - y_a) - (y_b - y_a) * (x_c - x_a);
   70 
   71   if(delta > 0)
   72     return -1;
   73   else if(delta < 0)
   74     return 1;
   75   else
   76     return 0;
   77 }
   78 
   79 static int dt_ellipse_point_in_polygon(float x, float y, float *points, int points_count)
   80 {
   81   int t = -1;
   82 
   83   t *= dt_ellipse_cross_test(x, y, points + 2 * (points_count - 1), points);
   84 
   85   for(int i = 0; i < points_count - 2; i++)
   86     t *= dt_ellipse_cross_test(x, y, points + 2 * i, points + 2 * (i + 1));
   87 
   88   return t;
   89 }
   90 
   91 // check if point is close to path - segment by segment
   92 static int dt_ellipse_point_close_to_path(float x, float y, float as, float *points, int points_count)
   93 {
   94   float as2 = as * as;
   95 
   96   float lastx = points[2 * (points_count - 1)];
   97   float lasty = points[2 * (points_count - 1) + 1];
   98 
   99   for(int i = 0; i < points_count; i++)
  100   {
  101     float px = points[2 * i];
  102     float py = points[2 * i + 1];
  103 
  104     float r1 = x - lastx;
  105     float r2 = y - lasty;
  106     float r3 = px - lastx;
  107     float r4 = py - lasty;
  108 
  109     float d = r1 * r3 + r2 * r4;
  110     float l = r3 * r3 + r4 * r4;
  111     float p = d / l;
  112 
  113     float xx, yy;
  114 
  115     if(p < 0 || (px == lastx && py == lasty))
  116     {
  117       xx = lastx;
  118       yy = lasty;
  119     }
  120     else if(p > 1)
  121     {
  122       xx = px;
  123       yy = py;
  124     }
  125     else
  126     {
  127       xx = lastx + p * r3;
  128       yy = lasty + p * r4;
  129     }
  130 
  131     float dx = x - xx;
  132     float dy = y - yy;
  133 
  134     if(dx * dx + dy * dy < as2) return 1;
  135   }
  136   return 0;
  137 }
  138 
  139 static void dt_ellipse_get_distance(float x, int y, float as, dt_masks_form_gui_t *gui, int index,
  140                                     int *inside, int *inside_border, int *near, int *inside_source)
  141 {
  142   if(!gui) return;
  143 
  144   dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index);
  145   if(!gpt) return;
  146 
  147 
  148   // we first check if we are inside the source form
  149   if(gpt->source_count > 10)
  150   {
  151     if(dt_ellipse_point_in_polygon(x, y, gpt->source + 10, gpt->source_count - 5) >= 0)
  152     {
  153       *inside_source = 1;
  154       *inside = 1;
  155       *inside_border = 0;
  156       *near = -1;
  157       return;
  158     }
  159   }
  160 
  161   *inside_source = 0;
  162 
  163   // we check if it's inside borders
  164   if(dt_ellipse_point_in_polygon(x, y, gpt->border + 10, gpt->border_count - 5) < 0)
  165   {
  166     *inside = 0;
  167     *inside_border = 0;
  168     *near = -1;
  169     return;
  170   }
  171 
  172   *inside = 1;
  173   *near = 0;
  174   *inside_border = 1;
  175 
  176   if(dt_ellipse_point_in_polygon(x, y, gpt->points + 10, gpt->points_count - 5) >= 0) *inside_border = 0;
  177   if(dt_ellipse_point_close_to_path(x, y, as, gpt->points + 10, gpt->points_count - 5)) *near = 1;
  178 }
  179 
  180 static void dt_ellipse_draw_shape(cairo_t *cr, double *dashed, const int selected, const float zoom_scale,
  181                                   const float dx, const float dy, const float xref, const float yref,
  182                                   const float sinv, const float cosv, const float scalea, const float scaleb,
  183                                   float *points, const int points_count)
  184 {
  185   if(points_count <= 10) return;
  186 
  187   const float r = atan2(points[3] - points[1], points[2] - points[0]);
  188   const float sinr = sin(r);
  189   const float cosr = cos(r);
  190 
  191   float x = 0.f;
  192   float y = 0.f;
  193 
  194   cairo_set_dash(cr, dashed, 0, 0);
  195   if(selected)
  196     cairo_set_line_width(cr, 5.0 / zoom_scale);
  197   else
  198     cairo_set_line_width(cr, 3.0 / zoom_scale);
  199   cairo_set_source_rgba(cr, .3, .3, .3, .8);
  200 
  201   _ellipse_point_transform(xref, yref, points[10] + dx, points[11] + dy, sinr, cosr, scalea, scaleb, sinv, cosv,
  202                            &x, &y);
  203   cairo_move_to(cr, x, y);
  204   for(int i = 6; i < points_count; i++)
  205   {
  206     _ellipse_point_transform(xref, yref, points[i * 2] + dx, points[i * 2 + 1] + dy, sinr, cosr, scalea, scaleb,
  207                              sinv, cosv, &x, &y);
  208     cairo_line_to(cr, x, y);
  209   }
  210   _ellipse_point_transform(xref, yref, points[10] + dx, points[11] + dy, sinr, cosr, scalea, scaleb, sinv, cosv,
  211                            &x, &y);
  212   cairo_line_to(cr, x, y);
  213   cairo_stroke_preserve(cr);
  214   if(selected)
  215     cairo_set_line_width(cr, 2.0 / zoom_scale);
  216   else
  217     cairo_set_line_width(cr, 1.0 / zoom_scale);
  218   cairo_set_source_rgba(cr, .8, .8, .8, .8);
  219   cairo_stroke(cr);
  220 }
  221 
  222 static void dt_ellipse_draw_border(cairo_t *cr, double *dashed, const float len, const int selected,
  223                                    const float zoom_scale, const float dx, const float dy, const float xref,
  224                                    const float yref, const float sinv, const float cosv, const float scaleab,
  225                                    const float scalebb, float *border, const int border_count)
  226 {
  227   if(border_count <= 10) return;
  228 
  229   const float r = atan2(border[3] - border[1], border[2] - border[0]);
  230   const float sinr = sin(r);
  231   const float cosr = cos(r);
  232 
  233   float x = 0.f;
  234   float y = 0.f;
  235 
  236   cairo_set_dash(cr, dashed, len, 0);
  237   if(selected)
  238     cairo_set_line_width(cr, 2.0 / zoom_scale);
  239   else
  240     cairo_set_line_width(cr, 1.0 / zoom_scale);
  241   cairo_set_source_rgba(cr, .3, .3, .3, .8);
  242 
  243   _ellipse_point_transform(xref, yref, border[10] + dx, border[11] + dy, sinr, cosr, scaleab, scalebb, sinv, cosv,
  244                            &x, &y);
  245   cairo_move_to(cr, x, y);
  246   for(int i = 6; i < border_count; i++)
  247   {
  248     _ellipse_point_transform(xref, yref, border[i * 2] + dx, border[i * 2 + 1] + dy, sinr, cosr, scaleab, scalebb,
  249                              sinv, cosv, &x, &y);
  250     cairo_line_to(cr, x, y);
  251   }
  252   _ellipse_point_transform(xref, yref, border[10] + dx, border[11] + dy, sinr, cosr, scaleab, scalebb, sinv, cosv,
  253                            &x, &y);
  254   cairo_line_to(cr, x, y);
  255 
  256   cairo_stroke_preserve(cr);
  257   if(selected)
  258     cairo_set_line_width(cr, 2.0 / zoom_scale);
  259   else
  260     cairo_set_line_width(cr, 1.0 / zoom_scale);
  261   cairo_set_source_rgba(cr, .8, .8, .8, .8);
  262   cairo_set_dash(cr, dashed, len, 4);
  263   cairo_stroke(cr);
  264 }
  265 
  266 static int dt_ellipse_get_points(dt_develop_t *dev, float xx, float yy, float radius_a, float radius_b,
  267                                  float rotation, float **points, int *points_count)
  268 {
  269   const float wd = dev->preview_pipe->iwidth;
  270   const float ht = dev->preview_pipe->iheight;
  271   const float v1 = (rotation / 180.0f) * M_PI;
  272   const float v2 = (rotation - 90.0f) / 180.0f * M_PI;
  273   float a, b, v;
  274 
  275   if(radius_a >= radius_b)
  276   {
  277     a = radius_a * MIN(wd, ht);
  278     b = radius_b * MIN(wd, ht);
  279     v = v1;
  280   }
  281   else
  282   {
  283     a = radius_b * MIN(wd, ht);
  284     b = radius_a * MIN(wd, ht);
  285     v = v2;
  286   }
  287 
  288   const float sinv = sinf(v);
  289   const float cosv = cosf(v);
  290 
  291 
  292   // how many points do we need? we only take every nth point and rely on interpolation (only affecting GUI
  293   // anyhow)
  294   const int n = 10;
  295   const float lambda = (a - b) / (a + b);
  296   const int l = MAX(
  297       100, (int)((M_PI * (a + b)
  298                   * (1.0f + (3.0f * lambda * lambda) / (10.0f + sqrtf(4.0f - 3.0f * lambda * lambda)))) / n));
  299 
  300   // buffer allocations
  301   *points = calloc(2 * (l + 5), sizeof(float));
  302   *points_count = l + 5;
  303 
  304   // now we set the points
  305   const float x = (*points)[0] = xx * wd;
  306   const float y = (*points)[1] = yy * ht;
  307 
  308   (*points)[2] = x + a * cos(v);
  309   (*points)[3] = y + a * sin(v);
  310   (*points)[4] = x - a * cos(v);
  311   (*points)[5] = y - a * sin(v);
  312 
  313   (*points)[6] = x + b * cos(v - M_PI / 2.0f);
  314   (*points)[7] = y + b * sin(v - M_PI / 2.0f);
  315   (*points)[8] = x - b * cos(v - M_PI / 2.0f);
  316   (*points)[9] = y - b * sin(v - M_PI / 2.0f);
  317 
  318 
  319   for(int i = 5; i < l + 5; i++)
  320   {
  321     float alpha = (i - 5) * 2.0 * M_PI / (float)l;
  322     (*points)[i * 2] = x + a * cosf(alpha) * cosv - b * sinf(alpha) * sinv;
  323     (*points)[i * 2 + 1] = y + a * cosf(alpha) * sinv + b * sinf(alpha) * cosv;
  324   }
  325 
  326   // and we transform them with all distorted modules
  327   if(dt_dev_distort_transform(dev, *points, l + 5)) return 1;
  328 
  329   // if we failed, then free all and return
  330   free(*points);
  331   *points = NULL;
  332   *points_count = 0;
  333   return 0;
  334 }
  335 
  336 static int dt_ellipse_events_mouse_scrolled(struct dt_iop_module_t *module, float pzx, float pzy, int up,
  337                                             uint32_t state, dt_masks_form_t *form, int parentid,
  338                                             dt_masks_form_gui_t *gui, int index)
  339 {
  340   // add a preview when creating an ellipse
  341   if(gui->creation)
  342   {
  343     if((state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) == (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
  344     {
  345       float rotation;
  346 
  347       if(form->type & (DT_MASKS_CLONE | DT_MASKS_NON_CLONE))
  348         rotation = dt_conf_get_float("plugins/darkroom/spots/ellipse_rotation");
  349       else
  350         rotation = dt_conf_get_float("plugins/darkroom/masks/ellipse/rotation");
  351 
  352       if(up)
  353         rotation -= 10.f;
  354       else
  355         rotation += 10.f;
  356       rotation = fmodf(rotation, 360.0f);
  357 
  358       if(form->type & (DT_MASKS_CLONE | DT_MASKS_NON_CLONE))
  359         dt_conf_set_float("plugins/darkroom/spots/ellipse_rotation", rotation);
  360       else
  361         dt_conf_set_float("plugins/darkroom/masks/ellipse/rotation", rotation);
  362     }
  363     else if((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
  364     {
  365       float masks_border;
  366       int flags;
  367       float radius_a;
  368       float radius_b;
  369 
  370       if(form->type & (DT_MASKS_CLONE | DT_MASKS_NON_CLONE))
  371       {
  372         masks_border = dt_conf_get_float("plugins/darkroom/spots/ellipse_border");
  373         flags = dt_conf_get_int("plugins/darkroom/spots/ellipse_flags");
  374         radius_a = dt_conf_get_float("plugins/darkroom/spots/ellipse_radius_a");
  375         radius_b = dt_conf_get_float("plugins/darkroom/spots/ellipse_radius_b");
  376       }
  377       else
  378       {
  379         masks_border = dt_conf_get_float("plugins/darkroom/masks/ellipse/border");
  380         flags = dt_conf_get_int("plugins/darkroom/masks/ellipse/flags");
  381         radius_a = dt_conf_get_float("plugins/darkroom/masks/ellipse/radius_a");
  382         radius_b = dt_conf_get_float("plugins/darkroom/masks/ellipse/radius_b");
  383       }
  384 
  385       const float reference = (flags & DT_MASKS_ELLIPSE_PROPORTIONAL ? 1.0f / fmin(radius_a, radius_b) : 1.0f);
  386       if(up && masks_border > 0.001f * reference)
  387         masks_border *= 0.97f;
  388       else if(!up && masks_border < 1.0f * reference)
  389         masks_border *= 1.0f / 0.97f;
  390       else
  391         return 1;
  392       masks_border = CLAMP(masks_border, 0.001f * reference, reference);
  393 
  394       if(form->type & (DT_MASKS_CLONE | DT_MASKS_NON_CLONE))
  395         dt_conf_set_float("plugins/darkroom/spots/ellipse_border", masks_border);
  396       else
  397         dt_conf_set_float("plugins/darkroom/masks/ellipse/border", masks_border);
  398     }
  399     else if(state == 0)
  400     {
  401       float radius_a;
  402       float radius_b;
  403 
  404       if(form->type & (DT_MASKS_CLONE | DT_MASKS_NON_CLONE))
  405       {
  406         radius_a = dt_conf_get_float("plugins/darkroom/spots/ellipse_radius_a");
  407         radius_b = dt_conf_get_float("plugins/darkroom/spots/ellipse_radius_b");
  408       }
  409       else
  410       {
  411         radius_a = dt_conf_get_float("plugins/darkroom/masks/ellipse/radius_a");
  412         radius_b = dt_conf_get_float("plugins/darkroom/masks/ellipse/radius_b");
  413       }
  414 
  415       const float oldradius = radius_a;
  416 
  417       if(up && radius_a > 0.001f)
  418         radius_a *= 0.97f;
  419       else if(!up && radius_a < 1.0f)
  420         radius_a *= 1.0f / 0.97f;
  421       else
  422         return 1;
  423 
  424       radius_a = CLAMP(radius_a, 0.001f, 1.0f);
  425 
  426       const float factor = radius_a / oldradius;
  427       radius_b *= factor;
  428 
  429       if(form->type & (DT_MASKS_CLONE | DT_MASKS_NON_CLONE))
  430       {
  431         dt_conf_set_float("plugins/darkroom/spots/ellipse_radius_a", radius_a);
  432         dt_conf_set_float("plugins/darkroom/spots/ellipse_radius_b", radius_b);
  433       }
  434       else
  435       {
  436         dt_conf_set_float("plugins/darkroom/masks/ellipse/radius_a", radius_a);
  437         dt_conf_set_float("plugins/darkroom/masks/ellipse/radius_b", radius_b);
  438       }
  439     }
  440     return 1;
  441   }
  442 
  443   if(gui->form_selected)
  444   {
  445     // we register the current position
  446     if(gui->scrollx == 0.0f && gui->scrolly == 0.0f)
  447     {
  448       gui->scrollx = pzx;
  449       gui->scrolly = pzy;
  450     }
  451     if((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK && !((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK))
  452     {
  453       // we try to change the opacity
  454       dt_masks_form_change_opacity(form, parentid, up);
  455     }
  456     else
  457     {
  458       dt_masks_point_ellipse_t *ellipse = (dt_masks_point_ellipse_t *)(g_list_first(form->points)->data);
  459       if((state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) == (GDK_SHIFT_MASK | GDK_CONTROL_MASK)
  460          && gui->edit_mode == DT_MASKS_EDIT_FULL)
  461       {
  462         // we try to change the rotation
  463         if(up)
  464           ellipse->rotation -= 10.f;
  465         else
  466           ellipse->rotation += 10.f;
  467         ellipse->rotation = fmodf(ellipse->rotation, 360.0f);
  468 
  469         dt_masks_write_form(form, darktable.develop);
  470         dt_masks_gui_form_remove(form, gui, index);
  471         dt_masks_gui_form_create(form, gui, index);
  472         if(form->type & (DT_MASKS_CLONE | DT_MASKS_NON_CLONE))
  473           dt_conf_set_float("plugins/darkroom/spots/ellipse_rotation", ellipse->rotation);
  474         else
  475           dt_conf_set_float("plugins/darkroom/masks/ellipse/rotation", ellipse->rotation);
  476       }
  477       // resize don't care where the mouse is inside a shape
  478       if((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
  479       {
  480         const float reference = (ellipse->flags & DT_MASKS_ELLIPSE_PROPORTIONAL ? 1.0f/fmin(ellipse->radius[0], ellipse->radius[1]) : 1.0f);
  481         if(up && ellipse->border > 0.001f * reference)
  482           ellipse->border *= 0.97f;
  483         else if(!up && ellipse->border < 1.0f * reference)
  484           ellipse->border *= 1.0f/0.97f;
  485         else return 1;
  486         ellipse->border = CLAMP(ellipse->border, 0.001f * reference, reference);
  487         dt_masks_write_form(form, darktable.develop);
  488         dt_masks_gui_form_remove(form, gui, index);
  489         dt_masks_gui_form_create(form, gui, index);
  490         if(form->type & (DT_MASKS_CLONE|DT_MASKS_NON_CLONE))
  491           dt_conf_set_float("plugins/darkroom/spots/ellipse_border", ellipse->border);
  492         else
  493           dt_conf_set_float("plugins/darkroom/masks/ellipse/border", ellipse->border);
  494       }
  495       else if(gui->edit_mode == DT_MASKS_EDIT_FULL)
  496       {
  497         const float oldradius = ellipse->radius[0];
  498 
  499         if(up && ellipse->radius[0] > 0.001f)
  500           ellipse->radius[0] *= 0.97f;
  501         else if(!up && ellipse->radius[0] < 1.0f)
  502           ellipse->radius[0] *= 1.0f / 0.97f;
  503         else return 1;
  504 
  505         ellipse->radius[0] = CLAMP(ellipse->radius[0], 0.001f, 1.0f);
  506 
  507         const float factor = ellipse->radius[0] / oldradius;
  508         ellipse->radius[1] *= factor;
  509 
  510         dt_masks_write_form(form, darktable.develop);
  511         dt_masks_gui_form_remove(form, gui, index);
  512         dt_masks_gui_form_create(form, gui, index);
  513         if(form->type & (DT_MASKS_CLONE|DT_MASKS_NON_CLONE))
  514         {
  515           dt_conf_set_float("plugins/darkroom/spots/ellipse_radius_a", ellipse->radius[0]);
  516           dt_conf_set_float("plugins/darkroom/spots/ellipse_radius_b", ellipse->radius[1]);
  517         }
  518         else
  519         {
  520           dt_conf_set_float("plugins/darkroom/masks/ellipse/radius_a", ellipse->radius[0]);
  521           dt_conf_set_float("plugins/darkroom/masks/ellipse/radius_b", ellipse->radius[1]);
  522         }
  523       }
  524       else
  525       {
  526         return 0;
  527       }
  528       dt_masks_update_image(darktable.develop);
  529     }
  530     return 1;
  531   }
  532   return 0;
  533 }
  534 
  535 static int dt_ellipse_events_button_pressed(struct dt_iop_module_t *module, float pzx, float pzy,
  536                                             double pressure, int which, int type, uint32_t state,
  537                                             dt_masks_form_t *form, int parentid, dt_masks_form_gui_t *gui,
  538                                             int index)
  539 {
  540   if(!gui) return 0;
  541   if(gui->source_selected && !gui->creation && gui->edit_mode == DT_MASKS_EDIT_FULL)
  542   {
  543     dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index);
  544     if(!gpt) return 0;
  545     // we start the source dragging
  546     gui->source_dragging = TRUE;
  547     gui->dx = gpt->source[0] - gui->posx;
  548     gui->dy = gpt->source[1] - gui->posy;
  549     return 1;
  550   }
  551   else if(gui->point_selected >= 1 && !gui->creation && gui->edit_mode == DT_MASKS_EDIT_FULL
  552           && !((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK))
  553   {
  554     dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index);
  555     if(!gpt) return 0;
  556     gui->point_dragging = gui->point_selected;
  557     gui->dx = gpt->points[0] - gui->posx;
  558     gui->dy = gpt->points[1] - gui->posy;
  559     return 1;
  560   }
  561   else if(gui->form_selected && !gui->creation && gui->edit_mode == DT_MASKS_EDIT_FULL
  562           && !((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK))
  563   {
  564     dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index);
  565     if(!gpt) return 0;
  566     // we start the form dragging or rotating
  567     if((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
  568       gui->form_rotating = TRUE;
  569     else
  570       gui->form_dragging = TRUE;
  571     gui->dx = gpt->points[0] - gui->posx;
  572     gui->dy = gpt->points[1] - gui->posy;
  573     return 1;
  574   }
  575   else if(gui->form_selected && !gui->creation && ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK))
  576   {
  577     dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index);
  578     if(!gpt) return 0;
  579 
  580     gui->border_toggling = TRUE;
  581 
  582     return 1;
  583   }
  584   else if(gui->creation && (which == 3))
  585   {
  586     gui->creation_continuous = FALSE;
  587     gui->creation_continuous_module = NULL;
  588     dt_masks_set_edit_mode(module, DT_MASKS_EDIT_FULL);
  589     dt_masks_iop_update(module);
  590     dt_control_queue_redraw_center();
  591     return 1;
  592   }
  593   else if(gui->creation && which == 1
  594           && (((state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == (GDK_CONTROL_MASK | GDK_SHIFT_MASK))
  595               || ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)))
  596   {
  597     // set some absolute or relative position for the source of the clone mask
  598     if(form->type & DT_MASKS_CLONE) dt_masks_set_source_pos_initial_state(gui, state, pzx, pzy);
  599 
  600     return 1;
  601   }
  602   else if(gui->creation)
  603   {
  604     dt_iop_module_t *crea_module = gui->creation_module;
  605     // we create the ellipse
  606     dt_masks_point_ellipse_t *ellipse
  607         = (dt_masks_point_ellipse_t *)(malloc(sizeof(dt_masks_point_ellipse_t)));
  608 
  609     // we change the center value
  610     float wd = darktable.develop->preview_pipe->backbuf_width;
  611     float ht = darktable.develop->preview_pipe->backbuf_height;
  612     float pts[2] = { pzx * wd, pzy * ht };
  613     dt_dev_distort_backtransform(darktable.develop, pts, 1);
  614     ellipse->center[0] = pts[0] / darktable.develop->preview_pipe->iwidth;
  615     ellipse->center[1] = pts[1] / darktable.develop->preview_pipe->iheight;
  616 
  617     if(form->type & (DT_MASKS_CLONE|DT_MASKS_NON_CLONE))
  618     {
  619       const float a = dt_conf_get_float("plugins/darkroom/spots/ellipse_radius_a");
  620       const float b = dt_conf_get_float("plugins/darkroom/spots/ellipse_radius_b");
  621       const float ellipse_border = dt_conf_get_float("plugins/darkroom/spots/ellipse_border");
  622       const float rotation = dt_conf_get_float("plugins/darkroom/spots/ellipse_rotation");
  623       const int flags = dt_conf_get_int("plugins/darkroom/spots/ellipse_flags");
  624       ellipse->radius[0] = MAX(0.001f, MIN(0.5f, a));
  625       ellipse->radius[1] = MAX(0.001f, MIN(0.5f, b));
  626       ellipse->flags = flags;
  627       ellipse->rotation = rotation;
  628       const float min_radius = fmin(ellipse->radius[0], ellipse->radius[1]);
  629       const float reference = (ellipse->flags & DT_MASKS_ELLIPSE_PROPORTIONAL ? 1.0f/min_radius : 1.0f);
  630       ellipse->border = MAX(0.005f * reference, MIN(0.5f * reference, ellipse_border));
  631       if(form->type & DT_MASKS_CLONE)
  632       {
  633         dt_masks_set_source_pos_initial_value(gui, DT_MASKS_ELLIPSE, form, pzx, pzy);
  634       }
  635       else
  636       {
  637         // not used for regular masks
  638         form->source[0] = form->source[1] = 0.0f;
  639       }
  640     }
  641     else
  642     {
  643       const float a = dt_conf_get_float("plugins/darkroom/masks/ellipse/radius_a");
  644       const float b = dt_conf_get_float("plugins/darkroom/masks/ellipse/radius_b");
  645       const float ellipse_border = dt_conf_get_float("plugins/darkroom/masks/ellipse/border");
  646       const float rotation = dt_conf_get_float("plugins/darkroom/masks/ellipse/rotation");
  647       const int flags = dt_conf_get_int("plugins/darkroom/masks/ellipse/flags");
  648       ellipse->radius[0] = MAX(0.001f, MIN(0.5f, a));
  649       ellipse->radius[1] = MAX(0.001f, MIN(0.5f, b));
  650       ellipse->flags = flags;
  651       ellipse->rotation = rotation;
  652       const float min_radius = fmin(ellipse->radius[0], ellipse->radius[1]);
  653       const float reference = (ellipse->flags & DT_MASKS_ELLIPSE_PROPORTIONAL ? 1.0f/min_radius : 1.0f);
  654       ellipse->border = MAX(0.005f * reference, MIN(0.5f * reference, ellipse_border));
  655       // not used for masks
  656       form->source[0] = form->source[1] = 0.0f;
  657     }
  658     form->points = g_list_append(form->points, ellipse);
  659     dt_masks_gui_form_save_creation(darktable.develop, crea_module, form, gui);
  660 
  661     if(crea_module)
  662     {
  663       // we save the move
  664       dt_dev_add_history_item(darktable.develop, crea_module, TRUE);
  665       // and we switch in edit mode to show all the forms
  666       if(gui->creation_continuous)
  667         dt_masks_set_edit_mode_single_form(crea_module, form->formid, DT_MASKS_EDIT_FULL);
  668       else
  669         dt_masks_set_edit_mode(crea_module, DT_MASKS_EDIT_FULL);
  670       dt_masks_iop_update(crea_module);
  671       gui->creation_module = NULL;
  672     }
  673     else
  674     {
  675       // we select the new form
  676       dt_dev_masks_selection_change(darktable.develop, form->formid, TRUE);
  677     }
  678 
  679     // if we draw a clone ellipse, we start now the source dragging
  680     if(form->type & (DT_MASKS_CLONE|DT_MASKS_NON_CLONE))
  681     {
  682       dt_masks_form_t *grp = darktable.develop->form_visible;
  683       if(!grp || !(grp->type & DT_MASKS_GROUP)) return 1;
  684       int pos3 = 0, pos2 = -1;
  685       GList *fs = g_list_first(grp->points);
  686       while(fs)
  687       {
  688         dt_masks_point_group_t *pt = (dt_masks_point_group_t *)fs->data;
  689         if(pt->formid == form->formid)
  690         {
  691           pos2 = pos3;
  692           break;
  693         }
  694         pos3++;
  695         fs = g_list_next(fs);
  696       }
  697       if(pos2 < 0) return 1;
  698       dt_masks_form_gui_t *gui2 = darktable.develop->form_gui;
  699       if(!gui2) return 1;
  700       if(form->type & DT_MASKS_CLONE)
  701         gui2->source_dragging = TRUE;
  702       else
  703         gui2->form_dragging = TRUE;
  704       gui2->group_edited = gui2->group_selected = pos2;
  705       gui2->posx = pzx * darktable.develop->preview_pipe->backbuf_width;
  706       gui2->posy = pzy * darktable.develop->preview_pipe->backbuf_height;
  707       gui2->dx = 0.0;
  708       gui2->dy = 0.0;
  709       gui2->scrollx = pzx;
  710       gui2->scrolly = pzy;
  711       gui2->form_selected = TRUE; // we also want to be selected after button released
  712 
  713       dt_masks_select_form(module, dt_masks_get_from_id(darktable.develop, form->formid));
  714     }
  715     return 1;
  716   }
  717   return 0;
  718 }
  719 
  720 static int dt_ellipse_events_button_released(struct dt_iop_module_t *module, float pzx, float pzy, int which,
  721                                              uint32_t state, dt_masks_form_t *form, int parentid,
  722                                              dt_masks_form_gui_t *gui, int index)
  723 {
  724   if(which == 3 && parentid > 0 && gui->edit_mode == DT_MASKS_EDIT_FULL)
  725   {
  726     // we hide the form
  727     if(!(darktable.develop->form_visible->type & DT_MASKS_GROUP))
  728       dt_masks_change_form_gui(NULL);
  729     else if(g_list_length(darktable.develop->form_visible->points) < 2)
  730       dt_masks_change_form_gui(NULL);
  731     else
  732     {
  733       dt_masks_clear_form_gui(darktable.develop);
  734       GList *forms = g_list_first(darktable.develop->form_visible->points);
  735       while(forms)
  736       {
  737         dt_masks_point_group_t *gpt = (dt_masks_point_group_t *)forms->data;
  738         if(gpt->formid == form->formid)
  739         {
  740           darktable.develop->form_visible->points
  741               = g_list_remove(darktable.develop->form_visible->points, gpt);
  742           free(gpt);
  743           break;
  744         }
  745         forms = g_list_next(forms);
  746       }
  747       gui->edit_mode = DT_MASKS_EDIT_FULL;
  748     }
  749 
  750     // we remove the shape
  751     dt_dev_masks_list_remove(darktable.develop, form->formid, parentid);
  752     dt_masks_form_remove(module, dt_masks_get_from_id(darktable.develop, parentid), form);
  753     return 1;
  754   }
  755   if(gui->form_dragging)
  756   {
  757     // we get the ellipse
  758     dt_masks_point_ellipse_t *ellipse = (dt_masks_point_ellipse_t *)(g_list_first(form->points)->data);
  759 
  760     // we end the form dragging
  761     gui->form_dragging = FALSE;
  762 
  763     // we change the center value
  764     float wd = darktable.develop->preview_pipe->backbuf_width;
  765     float ht = darktable.develop->preview_pipe->backbuf_height;
  766     float pts[2] = { pzx * wd + gui->dx, pzy * ht + gui->dy };
  767     dt_dev_distort_backtransform(darktable.develop, pts, 1);
  768     ellipse->center[0] = pts[0] / darktable.develop->preview_pipe->iwidth;
  769     ellipse->center[1] = pts[1] / darktable.develop->preview_pipe->iheight;
  770     dt_masks_write_form(form, darktable.develop);
  771 
  772     // we recreate the form points
  773     dt_masks_gui_form_remove(form, gui, index);
  774     dt_masks_gui_form_create(form, gui, index);
  775 
  776     // we save the move
  777     dt_masks_update_image(darktable.develop);
  778 
  779     if(gui->creation_continuous)
  780     {
  781       dt_masks_form_t *form_new = dt_masks_create(form->type);
  782       dt_masks_change_form_gui(form_new);
  783 
  784       darktable.develop->form_gui->creation = TRUE;
  785       darktable.develop->form_gui->creation_module = gui->creation_continuous_module;
  786     }
  787     return 1;
  788   }
  789   else if(gui->border_toggling)
  790   {
  791     // we get the ellipse
  792     dt_masks_point_ellipse_t *ellipse = (dt_masks_point_ellipse_t *)(g_list_first(form->points)->data);
  793 
  794     // we end the border toggling
  795     gui->border_toggling = FALSE;
  796 
  797     // toggle feathering type of border and adjust border radius accordingly
  798     if(ellipse->flags & DT_MASKS_ELLIPSE_PROPORTIONAL)
  799     {
  800       const float min_radius = fmin(ellipse->radius[0], ellipse->radius[1]);
  801       ellipse->border = ellipse->border * min_radius;
  802       ellipse->border = CLAMP(ellipse->border, 0.001f, 1.0f);
  803 
  804       ellipse->flags &= ~DT_MASKS_ELLIPSE_PROPORTIONAL;
  805     }
  806     else
  807     {
  808       const float min_radius = fmin(ellipse->radius[0], ellipse->radius[1]);
  809       ellipse->border = ellipse->border/min_radius;
  810       ellipse->border = CLAMP(ellipse->border, 0.001f/min_radius, 1.0f/min_radius);
  811 
  812       ellipse->flags |= DT_MASKS_ELLIPSE_PROPORTIONAL;
  813     }
  814 
  815     if(form->type & (DT_MASKS_CLONE|DT_MASKS_NON_CLONE))
  816     {
  817       dt_conf_set_int("plugins/darkroom/spots/ellipse_flags", ellipse->flags);
  818       dt_conf_set_float("plugins/darkroom/spots/ellipse_border", ellipse->border);
  819     }
  820     else
  821     {
  822       dt_conf_set_int("plugins/darkroom/masks/ellipse/flags", ellipse->flags);
  823       dt_conf_set_float("plugins/darkroom/masks/ellipse/border", ellipse->border);
  824     }
  825 
  826     dt_masks_write_form(form, darktable.develop);
  827 
  828     // we recreate the form points
  829     dt_masks_gui_form_remove(form, gui, index);
  830     dt_masks_gui_form_create(form, gui, index);
  831 
  832     // we save the new parameters
  833     dt_masks_update_image(darktable.develop);
  834 
  835     return 1;
  836   }
  837   else if(gui->form_rotating && gui->edit_mode == DT_MASKS_EDIT_FULL)
  838   {
  839     // we get the ellipse
  840     dt_masks_point_ellipse_t *ellipse = (dt_masks_point_ellipse_t *)(g_list_first(form->points)->data);
  841 
  842     // we end the form rotating
  843     gui->form_rotating = FALSE;
  844 
  845     float wd = darktable.develop->preview_pipe->backbuf_width;
  846     float ht = darktable.develop->preview_pipe->backbuf_height;
  847     float x = pzx * wd;
  848     float y = pzy * ht;
  849 
  850     // we need the reference point
  851     dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index);
  852     if(!gpt) return 0;
  853     float xref = gpt->points[0];
  854     float yref = gpt->points[1];
  855 
  856     float dv = atan2(y - yref, x - xref) - atan2(-gui->dy, -gui->dx);
  857 
  858     ellipse->rotation += dv / M_PI * 180.0f;
  859     ellipse->rotation = fmodf(ellipse->rotation, 360.0f);
  860 
  861     if(form->type & (DT_MASKS_CLONE|DT_MASKS_NON_CLONE))
  862       dt_conf_set_float("plugins/darkroom/spots/ellipse_rotation", ellipse->rotation);
  863     else
  864       dt_conf_set_float("plugins/darkroom/masks/ellipse/rotation", ellipse->rotation);
  865 
  866     dt_masks_write_form(form, darktable.develop);
  867 
  868     // we recreate the form points
  869     dt_masks_gui_form_remove(form, gui, index);
  870     dt_masks_gui_form_create(form, gui, index);
  871 
  872     // we save the rotation
  873     dt_masks_update_image(darktable.develop);
  874 
  875     return 1;
  876   }
  877   else if(gui->point_dragging >= 1 && gui->edit_mode == DT_MASKS_EDIT_FULL)
  878   {
  879     // we get the ellipse
  880     dt_masks_point_ellipse_t *ellipse = (dt_masks_point_ellipse_t *)(g_list_first(form->points)->data);
  881 
  882     const int k = gui->point_dragging;
  883 
  884     // we end the point dragging
  885     gui->point_dragging = -1;
  886 
  887     // we need the reference points
  888     dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index);
  889     if(!gpt) return 0;
  890 
  891     const float xref = gpt->points[0];
  892     const float yref = gpt->points[1];
  893     const float rx = gpt->points[k * 2] - xref;
  894     const float ry = gpt->points[k * 2 + 1] - yref;
  895     const float deltax = gui->posx + gui->dx - xref;
  896     const float deltay = gui->posy + gui->dy - yref;
  897 
  898     const float r = sqrtf(rx * rx + ry * ry);
  899     const float d = (rx * deltax + ry * deltay) / r;
  900     const float s = fmaxf(r > 0.0f ? (r + d) / r : 0.0f, 0.0f);
  901 
  902     // make sure we adjust the right radius: anchor points and 1 and 2 correspond to the ellipse's longer axis
  903     if(((k == 1 || k == 2) && ellipse->radius[0] > ellipse->radius[1])
  904        || ((k == 3 || k == 4) && ellipse->radius[0] <= ellipse->radius[1]))
  905     {
  906       ellipse->radius[0] = MAX(0.002f, ellipse->radius[0] * s);
  907       if(form->type & (DT_MASKS_CLONE|DT_MASKS_NON_CLONE))
  908         dt_conf_set_float("plugins/darkroom/spots/ellipse_radius_a", ellipse->radius[0]);
  909       else
  910         dt_conf_set_float("plugins/darkroom/masks/ellipse/radius_a", ellipse->radius[0]);
  911     }
  912     else
  913     {
  914       ellipse->radius[1] = MAX(0.002f, ellipse->radius[1] * s);
  915       if(form->type & (DT_MASKS_CLONE|DT_MASKS_NON_CLONE))
  916         dt_conf_set_float("plugins/darkroom/spots/ellipse_radius_b", ellipse->radius[1]);
  917       else
  918         dt_conf_set_float("plugins/darkroom/masks/ellipse/radius_b", ellipse->radius[1]);
  919     }
  920 
  921     dt_masks_write_form(form, darktable.develop);
  922     // we recreate the form points
  923     dt_masks_gui_form_remove(form, gui, index);
  924     dt_masks_gui_form_create(form, gui, index);
  925 
  926     // we save the rotation
  927     dt_masks_update_image(darktable.develop);
  928 
  929     return 1;
  930   }
  931   else if(gui->source_dragging)
  932   {
  933     // we end the form dragging
  934     gui->source_dragging = FALSE;
  935     if(gui->scrollx != 0.0 || gui->scrolly != 0.0)
  936     {
  937       // if there's no dragging the source is calculated in dt_ellipse_events_button_pressed()
  938     }
  939     else
  940     {
  941       // we change the center value
  942       float wd = darktable.develop->preview_pipe->backbuf_width;
  943       float ht = darktable.develop->preview_pipe->backbuf_height;
  944       float pts[2] = { pzx * wd + gui->dx, pzy * ht + gui->dy };
  945 
  946       dt_dev_distort_backtransform(darktable.develop, pts, 1);
  947 
  948       form->source[0] = pts[0] / darktable.develop->preview_pipe->iwidth;
  949       form->source[1] = pts[1] / darktable.develop->preview_pipe->iheight;
  950     }
  951     dt_masks_write_form(form, darktable.develop);
  952 
  953     // we recreate the form points
  954     dt_masks_gui_form_remove(form, gui, index);
  955     dt_masks_gui_form_create(form, gui, index);
  956 
  957     // we save the move
  958     dt_masks_update_image(darktable.develop);
  959 
  960     if(gui->creation_continuous)
  961     {
  962       dt_masks_form_t *form_new = dt_masks_create(form->type);
  963       dt_masks_change_form_gui(form_new);
  964 
  965       darktable.develop->form_gui->creation = TRUE;
  966       darktable.develop->form_gui->creation_module = gui->creation_continuous_module;
  967     }
  968     return 1;
  969   }
  970   return 0;
  971 }
  972 
  973 static int dt_ellipse_events_mouse_moved(struct dt_iop_module_t *module, float pzx, float pzy,
  974                                          double pressure, int which, dt_masks_form_t *form, int parentid,
  975                                          dt_masks_form_gui_t *gui, int index)
  976 {
  977   if(gui->form_dragging || gui->form_rotating || gui->source_dragging || gui->point_dragging >= 1)
  978   {
  979     dt_control_queue_redraw_center();
  980     return 1;
  981   }
  982   else if(!gui->creation)
  983   {
  984     dt_dev_zoom_t zoom = dt_control_get_dev_zoom();
  985     int closeup = dt_control_get_dev_closeup();
  986     float zoom_scale = dt_dev_get_zoom_scale(darktable.develop, zoom, 1<<closeup, 1);
  987     float as = 0.005f / zoom_scale * darktable.develop->preview_pipe->backbuf_width;
  988     float x = pzx * darktable.develop->preview_pipe->backbuf_width;
  989     float y = pzy * darktable.develop->preview_pipe->backbuf_height;
  990     int in = 0, inb = 0, near = 0, ins = 0; // FIXME gcc7 false-positive
  991     dt_ellipse_get_distance(pzx * darktable.develop->preview_pipe->backbuf_width,
  992                             pzy * darktable.develop->preview_pipe->backbuf_height, as, gui, index, &in, &inb,
  993                             &near, &ins);
  994     if(ins)
  995     {
  996       gui->form_selected = TRUE;
  997       gui->source_selected = TRUE;
  998       gui->border_selected = FALSE;
  999     }
 1000     else if(inb)
 1001     {
 1002       gui->form_selected = TRUE;
 1003       gui->border_selected = TRUE;
 1004       gui->source_selected = FALSE;
 1005     }
 1006     else if(in)
 1007     {
 1008       gui->form_selected = TRUE;
 1009       gui->border_selected = FALSE;
 1010       gui->source_selected = FALSE;
 1011     }
 1012     else
 1013     {
 1014       gui->form_selected = FALSE;
 1015       gui->border_selected = FALSE;
 1016       gui->source_selected = FALSE;
 1017     }
 1018 
 1019     // see if we are close to one of the anchor points
 1020     gui->point_selected = -1;
 1021     if(gui->form_selected)
 1022     {
 1023       dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index);
 1024       for(int i = 1; i < 5; i++)
 1025       {
 1026         if(x - gpt->points[i * 2] > -as && x - gpt->points[i * 2] < as && y - gpt->points[i * 2 + 1] > -as
 1027            && y - gpt->points[i * 2 + 1] < as)
 1028         {
 1029           gui->point_selected = i;
 1030           break;
 1031         }
 1032       }
 1033     }
 1034 
 1035     dt_control_queue_redraw_center();
 1036     if(!gui->form_selected && !gui->border_selected) return 0;
 1037     if(gui->edit_mode != DT_MASKS_EDIT_FULL) return 0;
 1038     return 1;
 1039   }
 1040   // add a preview when creating an ellipse
 1041   else if(gui->creation)
 1042   {
 1043     dt_control_queue_redraw_center();
 1044     return 1;
 1045   }
 1046 
 1047   return 0;
 1048 }
 1049 
 1050 static void dt_ellipse_events_post_expose(cairo_t *cr, float zoom_scale, dt_masks_form_gui_t *gui, int index)
 1051 {
 1052   double dashed[] = { 4.0, 4.0 };
 1053   dashed[0] /= zoom_scale;
 1054   dashed[1] /= zoom_scale;
 1055   int len = sizeof(dashed) / sizeof(dashed[0]);
 1056   dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index);
 1057 
 1058   float dx = 0.0f, dy = 0.0f, xref = 0.0f, yref = 0.0f;
 1059   float dxs = 0.0f, dys = 0.0f, xrefs = 0.0f, yrefs = 0.0f;
 1060   float sinv = 0.0f, cosv = 1.0f;
 1061   float scalea = 1.0f, scaleb = 1.0f, scaleab = 1.0f, scalebb = 1.0f;
 1062 
 1063   // add a preview when creating an ellipse
 1064   // in creation mode
 1065   if(gui->creation)
 1066   {
 1067     if(gui->guipoints_count == 0)
 1068     {
 1069       dt_masks_form_t *form = darktable.develop->form_visible;
 1070       if(!form) return;
 1071 
 1072       float x, y;
 1073       float masks_border;
 1074       int flags;
 1075       float radius_a;
 1076       float radius_b;
 1077       float rotation;
 1078 
 1079       if(form->type & (DT_MASKS_CLONE | DT_MASKS_NON_CLONE))
 1080       {
 1081         masks_border = dt_conf_get_float("plugins/darkroom/spots/ellipse_border");
 1082         flags = dt_conf_get_int("plugins/darkroom/spots/ellipse_flags");
 1083         radius_a = dt_conf_get_float("plugins/darkroom/spots/ellipse_radius_a");
 1084         radius_b = dt_conf_get_float("plugins/darkroom/spots/ellipse_radius_b");
 1085         rotation = dt_conf_get_float("plugins/darkroom/spots/ellipse_rotation");
 1086       }
 1087       else
 1088       {
 1089         masks_border = dt_conf_get_float("plugins/darkroom/masks/ellipse/border");
 1090         flags = dt_conf_get_int("plugins/darkroom/masks/ellipse/flags");
 1091         radius_a = dt_conf_get_float("plugins/darkroom/masks/ellipse/radius_a");
 1092         radius_b = dt_conf_get_float("plugins/darkroom/masks/ellipse/radius_b");
 1093         rotation = dt_conf_get_float("plugins/darkroom/masks/ellipse/rotation");
 1094       }
 1095 
 1096       float pzx = gui->posx;
 1097       float pzy = gui->posy;
 1098 
 1099       if((pzx == -1.f && pzy == -1.f) || gui->mouse_leaved_center)
 1100       {
 1101         const float zoom_x = dt_control_get_dev_zoom_x();
 1102         const float zoom_y = dt_control_get_dev_zoom_y();
 1103         pzx = (.5f + zoom_x) * darktable.develop->preview_pipe->backbuf_width;
 1104         pzy = (.5f + zoom_y) * darktable.develop->preview_pipe->backbuf_height;
 1105       }
 1106 
 1107       float pts[2] = { pzx, pzy };
 1108       dt_dev_distort_backtransform(darktable.develop, pts, 1);
 1109       x = pts[0] / darktable.develop->preview_pipe->iwidth;
 1110       y = pts[1] / darktable.develop->preview_pipe->iheight;
 1111 
 1112       float *points = NULL;
 1113       int points_count = 0;
 1114       float *border = NULL;
 1115       int border_count = 0;
 1116 
 1117       int draw = 0;
 1118 
 1119       draw = dt_ellipse_get_points(darktable.develop, x, y, radius_a, radius_b, rotation, &points, &points_count);
 1120       if(draw && masks_border > 0.f)
 1121       {
 1122         draw = dt_ellipse_get_points(
 1123             darktable.develop, x, y,
 1124             (flags & DT_MASKS_ELLIPSE_PROPORTIONAL ? radius_a * (1.0f + masks_border) : radius_a + masks_border),
 1125             (flags & DT_MASKS_ELLIPSE_PROPORTIONAL ? radius_b * (1.0f + masks_border) : radius_b + masks_border),
 1126             rotation, &border, &border_count);
 1127       }
 1128 
 1129       if(draw && points_count >= 2)
 1130       {
 1131         xref = points[0];
 1132         yref = points[1];
 1133 
 1134         dt_ellipse_draw_shape(cr, dashed, 0, zoom_scale, dx, dy, xref, yref, sinv, cosv, scalea, scaleb, points,
 1135                               points_count);
 1136       }
 1137       if(draw && border_count >= 2)
 1138       {
 1139         xref = border[0];
 1140         yref = border[1];
 1141 
 1142         dt_ellipse_draw_border(cr, dashed, len, 0, zoom_scale, dx, dy, xref, yref, sinv, cosv, scaleab, scalebb,
 1143                                border, border_count);
 1144       }
 1145 
 1146       // draw a cross where the source will be created
 1147       if(form->type & DT_MASKS_CLONE)
 1148       {
 1149         float x = 0.f, y = 0.f;
 1150         dt_masks_calculate_source_pos_value(gui, DT_MASKS_ELLIPSE, pzx, pzy, pzx, pzy, &x, &y, FALSE);
 1151         dt_masks_draw_clone_source_pos(cr, zoom_scale, x, y);
 1152       }
 1153 
 1154       if(points) free(points);
 1155       if(border) free(border);
 1156     }
 1157     return;
 1158   }
 1159 
 1160   if(!gpt) return;
 1161 
 1162   const float r = atan2(gpt->points[3] - gpt->points[1], gpt->points[2] - gpt->points[0]);
 1163   const float sinr = sin(r);
 1164   const float cosr = cos(r);
 1165 
 1166   xref = gpt->points[0];
 1167   yref = gpt->points[1];
 1168 
 1169   if(gpt->source_count > 10)
 1170   {
 1171     xrefs = gpt->source[0];
 1172     yrefs = gpt->source[1];
 1173   }
 1174   if((gui->group_selected == index) && gui->form_dragging)
 1175   {
 1176     dx = gui->posx + gui->dx - xref;
 1177     dy = gui->posy + gui->dy - yref;
 1178   }
 1179   else if((gui->group_selected == index) && gui->source_dragging)
 1180   {
 1181     xrefs = gpt->source[0], yrefs = gpt->source[1];
 1182     dxs = gui->posx + gui->dx - xrefs;
 1183     dys = gui->posy + gui->dy - yrefs;
 1184   }
 1185   else if((gui->group_selected == index) && gui->form_rotating)
 1186   {
 1187     const float v = atan2(gui->posy - yref, gui->posx - xref) - atan2(-gui->dy, -gui->dx);
 1188     sinv = sin(v);
 1189     cosv = cos(v);
 1190   }
 1191   else if((gui->group_selected == index) && (gui->point_dragging >= 1))
 1192   {
 1193     const int k = gui->point_dragging;
 1194     const float rx = gpt->points[k * 2] - xref;
 1195     const float ry = gpt->points[k * 2 + 1] - yref;
 1196     const float bx = gpt->border[k * 2] - xref;
 1197     const float by = gpt->border[k * 2 + 1] - yref;
 1198     const float deltax = gui->posx + gui->dx - xref;
 1199     const float deltay = gui->posy + gui->dy - yref;
 1200 
 1201     const float r = sqrtf(rx * rx + ry * ry);
 1202     const float b = sqrtf(bx * bx + by * by);
 1203     float d = (rx * deltax + ry * deltay) / r;
 1204     if(r + d < 0) d = -r;
 1205 
 1206     if(k == 1 || k == 2)
 1207     {
 1208       scalea = r > 0 ? (r + d) / r : 0;
 1209       scaleab = b > 0 ? (b + d) / b : 0;
 1210     }
 1211     else
 1212     {
 1213       scaleb = r > 0 ? (r + d) / r : 0;
 1214       scalebb = b > 0 ? (b + d) / b : 0;
 1215     }
 1216   }
 1217 
 1218   float x, y;
 1219 
 1220   // draw shape
 1221   dt_ellipse_draw_shape(cr, dashed, 0, zoom_scale, dx, dy, xref, yref, sinv, cosv, scalea, scaleb, gpt->points,
 1222                         gpt->points_count);
 1223 
 1224   // draw anchor points
 1225   if(TRUE)
 1226   {
 1227     cairo_set_dash(cr, dashed, 0, 0);
 1228     float anchor_size; // = (gui->form_dragging || gui->form_selected) ? 7.0f / zoom_scale : 5.0f /
 1229                        // zoom_scale;
 1230 
 1231     for(int i = 1; i < 5; i++)
 1232     {
 1233       cairo_set_source_rgba(cr, .8, .8, .8, .8);
 1234 
 1235       if(i == gui->point_dragging || i == gui->point_selected)
 1236         anchor_size = 7.0f / zoom_scale;
 1237       else
 1238         anchor_size = 5.0f / zoom_scale;
 1239 
 1240       _ellipse_point_transform(xref, yref, gpt->points[i * 2] + dx, gpt->points[i * 2 + 1] + dy, sinr, cosr,
 1241                                scalea, scaleb, sinv, cosv, &x, &y);
 1242       cairo_rectangle(cr, x - (anchor_size * 0.5), y - (anchor_size * 0.5), anchor_size, anchor_size);
 1243       cairo_fill_preserve(cr);
 1244       if((gui->group_selected == index) && (i == gui->point_dragging || i == gui->point_selected))
 1245         cairo_set_line_width(cr, 2.0 / zoom_scale);
 1246       if((gui->group_selected == index) && (gui->form_dragging || gui->form_selected))
 1247         cairo_set_line_width(cr, 2.0 / zoom_scale);
 1248       else
 1249         cairo_set_line_width(cr, 1.0 / zoom_scale);
 1250       cairo_set_source_rgba(cr, .3, .3, .3, .8);
 1251       cairo_stroke(cr);
 1252     }
 1253   }
 1254 
 1255   // draw border
 1256   if(gui->group_selected == index)
 1257   {
 1258     dt_ellipse_draw_border(cr, dashed, len, 0, zoom_scale, dx, dy, xref, yref, sinv, cosv, scaleab, scalebb,
 1259                            gpt->border, gpt->border_count);
 1260   }
 1261 
 1262   // draw the source if any
 1263   if(gpt->source_count > 10)
 1264   {
 1265     // compute the dest inner ellipse intersection with the line from source center to dest center.
 1266     float cdx = gpt->source[0] + dxs - gpt->points[0] - dx;
 1267     float cdy = gpt->source[1] + dys - gpt->points[1] - dy;
 1268 
 1269     // we don't draw the line if source==point
 1270     if(cdx != 0.0 && cdy != 0.0)
 1271     {
 1272       cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
 1273       float cangle = atan(cdx / cdy);
 1274 
 1275       if(cdy > 0)
 1276         cangle = (M_PI / 2) - cangle;
 1277       else
 1278         cangle = -(M_PI / 2) - cangle;
 1279 
 1280       float arrowx = gpt->points[0] + dx;
 1281       float arrowy = gpt->points[1] + dy;
 1282 
 1283       cairo_move_to(cr, gpt->source[0] + dxs, gpt->source[1] + dys); // source center
 1284       cairo_line_to(cr, arrowx, arrowy);                             // dest border
 1285       // then draw to line for the arrow itself
 1286       const float arrow_scale = 8.0;
 1287       cairo_move_to(cr, arrowx + arrow_scale * cos(cangle + (0.4)),
 1288                     arrowy + arrow_scale * sin(cangle + (0.4)));
 1289       cairo_line_to(cr, arrowx, arrowy);
 1290       cairo_line_to(cr, arrowx + arrow_scale * cos(cangle - (0.4)),
 1291                     arrowy + arrow_scale * sin(cangle - (0.4)));
 1292 
 1293       cairo_set_dash(cr, dashed, 0, 0);
 1294       if((gui->group_selected == index) && (gui->form_selected || gui->form_dragging))
 1295         cairo_set_line_width(cr, 2.5 / zoom_scale);
 1296       else
 1297         cairo_set_line_width(cr, 1.5 / zoom_scale);
 1298       cairo_set_source_rgba(cr, .3, .3, .3, .8);
 1299       cairo_stroke_preserve(cr);
 1300       if((gui->group_selected == index) && (gui->form_selected || gui->form_dragging))
 1301         cairo_set_line_width(cr, 1.0 / zoom_scale);
 1302       else
 1303         cairo_set_line_width(cr, 0.5 / zoom_scale);
 1304       cairo_set_source_rgba(cr, .8, .8, .8, .8);
 1305       cairo_stroke(cr);
 1306     }
 1307 
 1308     // we draw the source
 1309     cairo_set_dash(cr, dashed, 0, 0);
 1310     if((gui->group_selected == index) && (gui->form_selected || gui->form_dragging))
 1311       cairo_set_line_width(cr, 2.5 / zoom_scale);
 1312     else
 1313       cairo_set_line_width(cr, 1.5 / zoom_scale);
 1314     cairo_set_source_rgba(cr, .3, .3, .3, .8);
 1315     _ellipse_point_transform(xrefs, yrefs, gpt->source[10] + dxs, gpt->source[11] + dys, sinr, cosr, scalea,
 1316                              scaleb, sinv, cosv, &x, &y);
 1317     cairo_move_to(cr, x, y);
 1318     for(int i = 6; i < gpt->source_count; i++)
 1319     {
 1320       _ellipse_point_transform(xrefs, yrefs, gpt->source[i * 2] + dxs, gpt->source[i * 2 + 1] + dys, sinr,
 1321                                cosr, scalea, scaleb, sinv, cosv, &x, &y);
 1322       cairo_line_to(cr, x, y);
 1323     }
 1324     _ellipse_point_transform(xrefs, yrefs, gpt->source[10] + dxs, gpt->source[11] + dys, sinr, cosr, scalea,
 1325                              scaleb, sinv, cosv, &x, &y);
 1326     cairo_line_to(cr, x, y);
 1327     cairo_stroke_preserve(cr);
 1328     if((gui->group_selected == index) && (gui->form_selected || gui->form_dragging))
 1329       cairo_set_line_width(cr, 1.0 / zoom_scale);
 1330     else
 1331       cairo_set_line_width(cr, 0.5 / zoom_scale);
 1332     cairo_set_source_rgba(cr, .8, .8, .8, .8);
 1333     cairo_stroke(cr);
 1334   }
 1335 }
 1336 
 1337 static int dt_ellipse_get_source_area(dt_iop_module_t *module, dt_dev_pixelpipe_iop_t *piece,
 1338                                       dt_masks_form_t *form, int *width, int *height, int *posx, int *posy)
 1339 {
 1340   // we get the ellipse values
 1341   dt_masks_point_ellipse_t *ellipse = (dt_masks_point_ellipse_t *)(g_list_first(form->points)->data);
 1342   const float wd = piece->pipe->iwidth, ht = piece->pipe->iheight;
 1343 
 1344   const float total[2] = { (ellipse->flags & DT_MASKS_ELLIPSE_PROPORTIONAL ? ellipse->radius[0] * (1.0f + ellipse->border) : ellipse->radius[0] + ellipse->border) * MIN(wd, ht),
 1345                            (ellipse->flags & DT_MASKS_ELLIPSE_PROPORTIONAL ? ellipse->radius[1] * (1.0f + ellipse->border) : ellipse->radius[1] + ellipse->border) * MIN(wd, ht) };
 1346   const float v1 = ((ellipse->rotation) / 180.0f) * M_PI;
 1347   const float v2 = ((ellipse->rotation - 90.0f) / 180.0f) * M_PI;
 1348   float a, b, v;
 1349 
 1350   if(total[0] >= total[1])
 1351   {
 1352     a = total[0];
 1353     b = total[1];
 1354     v = v1;
 1355   }
 1356   else
 1357   {
 1358     a = total[1];
 1359     b = total[0];
 1360     v = v2;
 1361   }
 1362 
 1363   const float sinv = sinf(v);
 1364   const float cosv = cosf(v);
 1365 
 1366   // how many points do we need ?
 1367   const float lambda = (a - b) / (a + b);
 1368   const int l = (int)(M_PI * (a + b)
 1369                       * (1.0f + (3.0f * lambda * lambda) / (10.0f + sqrtf(4.0f - 3.0f * lambda * lambda))));
 1370 
 1371   // buffer allocations
 1372   float *points = calloc(2 * (l + 5), sizeof(float));
 1373 
 1374   // now we set the points
 1375   const float x = points[0] = ellipse->center[0] * wd;
 1376   const float y = points[1] = ellipse->center[1] * ht;
 1377 
 1378   points[2] = x + a * cos(v);
 1379   points[3] = y + a * sin(v);
 1380   points[4] = x - a * cos(v);
 1381   points[5] = y - a * sin(v);
 1382 
 1383   points[6] = x + b * cos(v - M_PI / 2.0f);
 1384   points[7] = y + b * sin(v - M_PI / 2.0f);
 1385   points[8] = x - b * cos(v - M_PI / 2.0f);
 1386   points[9] = y - b * sin(v - M_PI / 2.0f);
 1387 
 1388   for(int i = 1; i < l + 5; i++)
 1389   {
 1390     float alpha = (i - 5) * 2.0 * M_PI / (float)l;
 1391     points[i * 2] = points[0] + a * cosf(alpha) * cosv - b * sinf(alpha) * sinv;
 1392     points[i * 2 + 1] = points[1] + a * cosf(alpha) * sinv + b * sinf(alpha) * cosv;
 1393   }
 1394 
 1395   // and we transform them with all distorted modules
 1396   if(!dt_dev_distort_transform_plus(darktable.develop, piece->pipe, 0, module->priority, points, l + 5))
 1397   {
 1398     free(points);
 1399     return 0;
 1400   }
 1401 
 1402   // now we search min and max
 1403   float xmin, xmax, ymin, ymax;
 1404   xmin = ymin = FLT_MAX;
 1405   xmax = ymax = FLT_MIN;
 1406   for(int i = 1; i < l + 5; i++)
 1407   {
 1408     xmin = fminf(points[i * 2], xmin);
 1409     xmax = fmaxf(points[i * 2], xmax);
 1410     ymin = fminf(points[i * 2 + 1], ymin);
 1411     ymax = fmaxf(points[i * 2 + 1], ymax);
 1412   }
 1413   free(points);
 1414   // and we set values
 1415   *posx = xmin;
 1416   *posy = ymin;
 1417   *width = (xmax - xmin);
 1418   *height = (ymax - ymin);
 1419   return 1;
 1420 }
 1421 
 1422 static int dt_ellipse_get_area(dt_iop_module_t *module, dt_dev_pixelpipe_iop_t *piece, dt_masks_form_t *form,
 1423                                int *width, int *height, int *posx, int *posy)
 1424 {
 1425   // we get the ellipse values
 1426   dt_masks_point_ellipse_t *ellipse = (dt_masks_point_ellipse_t *)(g_list_first(form->points)->data);
 1427 
 1428   const float wd = piece->pipe->iwidth, ht = piece->pipe->iheight;
 1429 
 1430   const float total[2] = { (ellipse->flags & DT_MASKS_ELLIPSE_PROPORTIONAL ? ellipse->radius[0] * (1.0f + ellipse->border) : ellipse->radius[0] + ellipse->border) * MIN(wd, ht),
 1431                            (ellipse->flags & DT_MASKS_ELLIPSE_PROPORTIONAL ? ellipse->radius[1] * (1.0f + ellipse->border) : ellipse->radius[1] + ellipse->border) * MIN(wd, ht) };
 1432   const float v1 = ((ellipse->rotation) / 180.0f) * M_PI;
 1433   const float v2 = ((ellipse->rotation - 90.0f) / 180.0f) * M_PI;
 1434   float a, b, v;
 1435 
 1436   if(total[0] >= total[1])
 1437   {
 1438     a = total[0];
 1439     b = total[1];
 1440     v = v1;
 1441   }
 1442   else
 1443   {
 1444     a = total[1];
 1445     b = total[0];
 1446     v = v2;
 1447   }
 1448 
 1449   const float sinv = sinf(v);
 1450   const float cosv = cosf(v);
 1451 
 1452   // how many points do we need ?
 1453   const float lambda = (a - b) / (a + b);
 1454   const int l = (int)(M_PI * (a + b)
 1455                       * (1.0f + (3.0f * lambda * lambda) / (10.0f + sqrtf(4.0f - 3.0f * lambda * lambda))));
 1456 
 1457   // buffer allocations
 1458   float *points = calloc(2 * (l + 5), sizeof(float));
 1459 
 1460   // now we set the points
 1461   const float x = points[0] = ellipse->center[0] * wd;
 1462   const float y = points[1] = ellipse->center[1] * ht;
 1463 
 1464   points[2] = x + a * cos(v);
 1465   points[3] = y + a * sin(v);
 1466   points[4] = x - a * cos(v);
 1467   points[5] = y - a * sin(v);
 1468 
 1469   points[6] = x + b * cos(v - M_PI / 2.0f);
 1470   points[7] = y + b * sin(v - M_PI / 2.0f);
 1471   points[8] = x - b * cos(v - M_PI / 2.0f);
 1472   points[9] = y - b * sin(v - M_PI / 2.0f);
 1473 
 1474   for(int i = 5; i < l + 5; i++)
 1475   {
 1476     float alpha = (i - 5) * 2.0 * M_PI / (float)l;
 1477     points[i * 2] = x + a * cosf(alpha) * cosv - b * sinf(alpha) * sinv;
 1478     points[i * 2 + 1] = y + a * cosf(alpha) * sinv + b * sinf(alpha) * cosv;
 1479   }
 1480 
 1481   // and we transform them with all distorted modules
 1482   if(!dt_dev_distort_transform_plus(module->dev, piece->pipe, 0, module->priority, points, l + 5))
 1483   {
 1484     free(points);
 1485     return 0;
 1486   }
 1487 
 1488   // now we search min and max
 1489   float xmin, xmax, ymin, ymax;
 1490   xmin = ymin = FLT_MAX;
 1491   xmax = ymax = FLT_MIN;
 1492   for(int i = 5; i < l + 5; i++)
 1493   {
 1494     xmin = fminf(points[i * 2], xmin);
 1495     xmax = fmaxf(points[i * 2], xmax);
 1496     ymin = fminf(points[i * 2 + 1], ymin);
 1497     ymax = fmaxf(points[i * 2 + 1], ymax);
 1498   }
 1499   free(points);
 1500 
 1501   // and we set values
 1502   *posx = xmin;
 1503   *posy = ymin;
 1504   *width = (xmax - xmin);
 1505   *height = (ymax - ymin);
 1506   return 1;
 1507 }
 1508 
 1509 static int dt_ellipse_get_mask(dt_iop_module_t *module, dt_dev_pixelpipe_iop_t *piece, dt_masks_form_t *form,
 1510                                float **buffer, int *width, int *height, int *posx, int *posy)
 1511 {
 1512   double start2 = dt_get_wtime();
 1513 
 1514   // we get the area
 1515   if(!dt_ellipse_get_area(module, piece, form, width, height, posx, posy)) return 0;
 1516 
 1517   if(darktable.unmuted & DT_DEBUG_PERF)
 1518     dt_print(DT_DEBUG_MASKS, "[masks %s] ellipse area took %0.04f sec\n", form->name, dt_get_wtime() - start2);
 1519   start2 = dt_get_wtime();
 1520 
 1521   // we get the ellipse values
 1522   dt_masks_point_ellipse_t *ellipse = (dt_masks_point_ellipse_t *)(g_list_first(form->points)->data);
 1523 
 1524   // we create a buffer of points with all points in the area
 1525   int w = *width, h = *height;
 1526   float *points = malloc(w * h * 2 * sizeof(float));
 1527   for(int i = 0; i < h; i++)
 1528     for(int j = 0; j < w; j++)
 1529     {
 1530       points[(i * w + j) * 2] = (j + (*posx));
 1531       points[(i * w + j) * 2 + 1] = (i + (*posy));
 1532     }
 1533 
 1534   if(darktable.unmuted & DT_DEBUG_PERF)
 1535     dt_print(DT_DEBUG_MASKS, "[masks %s] ellipse draw took %0.04f sec\n", form->name, dt_get_wtime() - start2);
 1536   start2 = dt_get_wtime();
 1537 
 1538   // we back transform all this points
 1539   if(!dt_dev_distort_backtransform_plus(module->dev, piece->pipe, 0, module->priority, points, w * h))
 1540   {
 1541     free(points);
 1542     return 0;
 1543   }
 1544 
 1545   if(darktable.unmuted & DT_DEBUG_PERF)
 1546     dt_print(DT_DEBUG_MASKS, "[masks %s] ellipse transform took %0.04f sec\n", form->name,
 1547              dt_get_wtime() - start2);
 1548   start2 = dt_get_wtime();
 1549 
 1550   // we allocate the buffer
 1551   *buffer = calloc(w * h, sizeof(float));
 1552 
 1553   // we populate the buffer
 1554   const int wi = piece->pipe->iwidth, hi = piece->pipe->iheight;
 1555   const float center[2] = { ellipse->center[0] * wi, ellipse->center[1] * hi };
 1556   const float radius[2] = { ellipse->radius[0] * MIN(wi, hi), ellipse->radius[1] * MIN(wi, hi) };
 1557   const float total[2] =  { (ellipse->flags & DT_MASKS_ELLIPSE_PROPORTIONAL ? ellipse->radius[0] * (1.0f + ellipse->border) : ellipse->radius[0] + ellipse->border) * MIN(wi, hi),
 1558                             (ellipse->flags & DT_MASKS_ELLIPSE_PROPORTIONAL ? ellipse->radius[1] * (1.0f + ellipse->border) : ellipse->radius[1] + ellipse->border) * MIN(wi, hi) };
 1559 
 1560   float a, b, ta, tb, alpha;
 1561 
 1562   if(radius[0] >= radius[1])
 1563   {
 1564     a = radius[0];
 1565     b = radius[1];
 1566     ta = total[0];
 1567     tb = total[1];
 1568     alpha = (ellipse->rotation / 180.0f) * M_PI;
 1569   }
 1570   else
 1571   {
 1572     a = radius[1];
 1573     b = radius[0];
 1574     ta = total[1];
 1575     tb = total[0];
 1576     alpha = ((ellipse->rotation - 90.0f) / 180.0f) * M_PI;
 1577   }
 1578 
 1579   for(int i = 0; i < h; i++)
 1580     for(int j = 0; j < w; j++)
 1581     {
 1582       float x = points[(i * w + j) * 2] - center[0];
 1583       float y = points[(i * w + j) * 2 + 1] - center[1];
 1584       float v = atan2(y, x) - alpha;
 1585       float cosv = cos(v);
 1586       float sinv = sin(v);
 1587       float radius2 = a * a * b * b / (a * a * sinv * sinv + b * b * cosv * cosv);
 1588       float total2 = ta * ta * tb * tb / (ta * ta * sinv * sinv + tb * tb * cosv * cosv);
 1589       float l2 = x * x + y * y;
 1590 
 1591       if(l2 < radius2)
 1592         (*buffer)[i * w + j] = 1.0f;
 1593       else if(l2 < total2)
 1594       {
 1595         float f = (total2 - l2) / (total2 - radius2);
 1596         (*buffer)[i * w + j] = f * f;
 1597       }
 1598       else
 1599         (*buffer)[i * w + j] = 0.0f;
 1600     }
 1601   free(points);
 1602 
 1603   if(darktable.unmuted & DT_DEBUG_PERF)
 1604     dt_print(DT_DEBUG_MASKS, "[masks %s] ellipse fill took %0.04f sec\n", form->name, dt_get_wtime() - start2);
 1605 //   start2 = dt_get_wtime();
 1606 
 1607   return 1;
 1608 }
 1609 
 1610 
 1611 static int dt_ellipse_get_mask_roi(dt_iop_module_t *module, dt_dev_pixelpipe_iop_t *piece,
 1612                                    dt_masks_form_t *form, const dt_iop_roi_t *roi, float *buffer)
 1613 {
 1614   double start2 = dt_get_wtime();
 1615 
 1616   // we get the ellipse values
 1617   dt_masks_point_ellipse_t *ellipse = (dt_masks_point_ellipse_t *)(g_list_first(form->points)->data);
 1618 
 1619   // we create a buffer of mesh points for later interpolation. mainly in order to reduce memory footprint
 1620   const int w = roi->width;
 1621   const int h = roi->height;
 1622   const int px = roi->x;
 1623   const int py = roi->y;
 1624   const float iscale = 1.0f / roi->scale;
 1625   const int mesh = 4;
 1626   const int mw = (w + mesh - 1) / mesh + 1;
 1627   const int mh = (h + mesh - 1) / mesh + 1;
 1628 
 1629   float *points = malloc((size_t)mw * mh * 2 * sizeof(float));
 1630   if(points == NULL) return 0;
 1631 
 1632 #ifdef _OPENMP
 1633 #if !defined(__SUNOS__) && !defined(__NetBSD__)
 1634 #pragma omp parallel for default(none) \
 1635   dt_omp_firstprivate(iscale, mh, mw, py, px, mesh)  \
 1636   shared(points)
 1637 #else
 1638 #pragma omp parallel for shared(points)
 1639 #endif
 1640 #endif
 1641   for(int j = 0; j < mh; j++)
 1642     for(int i = 0; i < mw; i++)
 1643     {
 1644       size_t index = (size_t)j * mw + i;
 1645       points[index * 2] = (mesh * i + px) * iscale;
 1646       points[index * 2 + 1] = (mesh * j + py) * iscale;
 1647     }
 1648 
 1649   if(darktable.unmuted & DT_DEBUG_PERF)
 1650     dt_print(DT_DEBUG_MASKS, "[masks %s] ellipse draw took %0.04f sec\n", form->name, dt_get_wtime() - start2);
 1651   start2 = dt_get_wtime();
 1652 
 1653   // we back transform all these points
 1654   if(!dt_dev_distort_backtransform_plus(module->dev, piece->pipe, 0, module->priority, points,
 1655                                         (size_t)mw * mh))
 1656   {
 1657     free(points);
 1658     return 0;
 1659   }
 1660 
 1661   if(darktable.unmuted & DT_DEBUG_PERF)
 1662     dt_print(DT_DEBUG_MASKS, "[masks %s] ellipse transform took %0.04f sec\n", form->name,
 1663              dt_get_wtime() - start2);
 1664   start2 = dt_get_wtime();
 1665 
 1666   // we populate the buffer
 1667   const int wi = piece->pipe->iwidth, hi = piece->pipe->iheight;
 1668   const float center[2] = { ellipse->center[0] * wi, ellipse->center[1] * hi };
 1669   const float radius[2] = { ellipse->radius[0] * MIN(wi, hi), ellipse->radius[1] * MIN(wi, hi) };
 1670   const float total[2] = { (ellipse->flags & DT_MASKS_ELLIPSE_PROPORTIONAL ? ellipse->radius[0] * (1.0f + ellipse->border) : ellipse->radius[0] + ellipse->border) * MIN(wi, hi),
 1671                            (ellipse->flags & DT_MASKS_ELLIPSE_PROPORTIONAL ? ellipse->radius[1] * (1.0f + ellipse->border) : ellipse->radius[1] + ellipse->border) * MIN(wi, hi) };
 1672 
 1673   float a, b, ta, tb, alpha;
 1674 
 1675   if(radius[0] >= radius[1])
 1676   {
 1677     a = radius[0];
 1678     b = radius[1];
 1679     ta = total[0];
 1680     tb = total[1];
 1681     alpha = (ellipse->rotation / 180.0f) * M_PI;
 1682   }
 1683   else
 1684   {
 1685     a = radius[1];
 1686     b = radius[0];
 1687     ta = total[1];
 1688     tb = total[0];
 1689     alpha = ((ellipse->rotation - 90.0f) / 180.0f) * M_PI;
 1690   }
 1691 
 1692 #ifdef _OPENMP
 1693 #if !defined(__SUNOS__) && !defined(__NetBSD__)
 1694 #pragma omp parallel for default(none) \
 1695   dt_omp_firstprivate(center, mh, mw, mesh) \
 1696   shared(points, a, b, ta, tb, alpha)
 1697 #else
 1698 #pragma omp parallel for shared(points, a, b, ta, tb, alpha)
 1699 #endif
 1700 #endif
 1701   for(int i = 0; i < mh; i++)
 1702     for(int j = 0; j < mw; j++)
 1703     {
 1704       size_t index = (size_t)i * mw + j;
 1705       float x = points[index * 2] - center[0];
 1706       float y = points[index * 2 + 1] - center[1];
 1707       float v = atan2(y, x) - alpha;
 1708       float cosv = cos(v);
 1709       float sinv = sin(v);
 1710       float radius2 = a * a * b * b / (a * a * sinv * sinv + b * b * cosv * cosv);
 1711       float total2 = ta * ta * tb * tb / (ta * ta * sinv * sinv + tb * tb * cosv * cosv);
 1712       float l2 = x * x + y * y;
 1713 
 1714       if(l2 < radius2)
 1715         points[index * 2] = 1.0f;
 1716       else if(l2 < total2)
 1717       {
 1718         float f = (total2 - l2) / (total2 - radius2);
 1719         points[index * 2] = f * f;
 1720       }
 1721       else
 1722         points[index * 2] = 0.0f;
 1723     }
 1724 
 1725 // we fill the output buffer by interpolation
 1726 #ifdef _OPENMP
 1727 #if !defined(__SUNOS__) && !defined(__NetBSD__)
 1728 #pragma omp parallel for default(none) \
 1729   dt_omp_firstprivate(h, mw, w, mesh)       \
 1730   shared(points, buffer)
 1731 #else
 1732 #pragma omp parallel for shared(points, buffer)
 1733 #endif
 1734 #endif
 1735   for(int j = 0; j < h; j++)
 1736   {
 1737     int jj = j % mesh;
 1738     int mj = j / mesh;
 1739     for(int i = 0; i < w; i++)
 1740     {
 1741       int ii = i % mesh;
 1742       int mi = i / mesh;
 1743       size_t mindex = (size_t)mj * mw + mi;
 1744       buffer[(size_t)j * w + i]
 1745           = (points[mindex * 2] * (mesh - ii) * (mesh - jj) + points[(mindex + 1) * 2] * ii * (mesh - jj)
 1746              + points[(mindex + mw) * 2] * (mesh - ii) * jj + points[(mindex + mw + 1) * 2] * ii * jj)
 1747             / (mesh * mesh);
 1748     }
 1749   }
 1750 
 1751   free(points);
 1752 
 1753 
 1754   if(darktable.unmuted & DT_DEBUG_PERF)
 1755     dt_print(DT_DEBUG_MASKS, "[masks %s] ellipse fill took %0.04f sec\n", form->name, dt_get_wtime() - start2);
 1756 //   start2 = dt_get_wtime();
 1757 
 1758   return 1;
 1759 }
 1760 
 1761 
 1762 
 1763 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
 1764 // vim: shiftwidth=2 expandtab tabstop=2 cindent
 1765 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;