// needed if mask dest is in roi and mask src is not void modify_roi_in(struct dt_iop_module_t *self, struct dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t *roi_out, dt_iop_roi_t *roi_in) { *roi_in = *roi_out; int roir = roi_in->width + roi_in->x; int roib = roi_in->height + roi_in->y; int roix = roi_in->x; int roiy = roi_in->y; // dt_iop_spots_params_t *d = (dt_iop_spots_params_t *)piece->data; dt_develop_blend_params_t *bp = self->blend_params; // We iterate through all spots or polygons dt_masks_form_t *grp = dt_masks_get_from_id(darktable.develop, bp->mask_id); if(grp && (grp->type & DT_MASKS_GROUP)) { GList *forms = g_list_first(grp->points); while(forms) { dt_masks_point_group_t *grpt = (dt_masks_point_group_t *)forms->data; // we get the spot dt_masks_form_t *form = dt_masks_get_from_id(self->dev, grpt->formid); if(form) { // if the form is outside the roi, we just skip it if(!masks_form_is_in_roi(self, piece, form, roi_in, roi_out)) { forms = g_list_next(forms); continue; } // we get the area for the source int fl, ft, fw, fh; if(!dt_masks_get_source_area(self, piece, form, &fw, &fh, &fl, &ft)) { forms = g_list_next(forms); continue; } fw *= roi_in->scale, fh *= roi_in->scale, fl *= roi_in->scale, ft *= roi_in->scale; // we enlarge the roi if needed roiy = fminf(ft, roiy); roix = fminf(fl, roix); roir = fmaxf(fl + fw, roir); roib = fmaxf(ft + fh, roib); } forms = g_list_next(forms); } } // now we set the values const float scwidth = piece->buf_in.width * roi_in->scale, scheight = piece->buf_in.height * roi_in->scale; roi_in->x = CLAMP(roix, 0, scwidth - 1); roi_in->y = CLAMP(roiy, 0, scheight - 1); roi_in->width = CLAMP(roir - roi_in->x, 1, scwidth + .5f - roi_in->x); roi_in->height = CLAMP(roib - roi_in->y, 1, scheight + .5f - roi_in->y); }
static int dt_group_events_button_pressed(struct dt_iop_module_t *module, float pzx, float pzy, double pressure, int which, int type, uint32_t state, dt_masks_form_t *form, dt_masks_form_gui_t *gui) { if (gui->group_edited != gui->group_selected) { //we set the selected form in edit mode gui->group_edited = gui->group_selected; //we initialise some variable gui->posx = gui->posy = gui->dx = gui->dy = 0.0f; gui->form_selected = gui->border_selected = gui->form_dragging = gui->form_rotating = FALSE; gui->pivot_selected = FALSE; gui->point_border_selected = gui->seg_selected = gui->point_selected = gui->feather_selected = -1; gui->point_border_dragging = gui->seg_dragging = gui->feather_dragging = gui->point_dragging = -1; dt_control_queue_redraw_center(); return 1; } if (gui->group_edited >= 0) { //we get the form dt_masks_point_group_t *fpt = (dt_masks_point_group_t *)g_list_nth_data(form->points,gui->group_edited); dt_masks_form_t *sel = dt_masks_get_from_id(darktable.develop,fpt->formid); if (!sel) return 0; if (sel->type & DT_MASKS_CIRCLE) return dt_circle_events_button_pressed(module,pzx,pzy,pressure,which,type,state,sel,fpt->parentid,gui,gui->group_edited); else if (sel->type & DT_MASKS_PATH) return dt_path_events_button_pressed(module,pzx,pzy,pressure,which,type,state,sel,fpt->parentid,gui,gui->group_edited); else if (sel->type & DT_MASKS_GRADIENT) return dt_gradient_events_button_pressed(module,pzx,pzy,pressure,which,type,state,sel,fpt->parentid,gui,gui->group_edited); else if (sel->type & DT_MASKS_ELLIPSE) return dt_ellipse_events_button_pressed(module,pzx,pzy,pressure,which,type,state,sel,fpt->parentid,gui,gui->group_edited); else if (sel->type & DT_MASKS_BRUSH) return dt_brush_events_button_pressed(module,pzx,pzy,pressure,which,type,state,sel,fpt->parentid,gui,gui->group_edited); } return 0; }
static int dt_group_events_button_released(struct dt_iop_module_t *module, float pzx, float pzy, int which, uint32_t state, dt_masks_form_t *form, dt_masks_form_gui_t *gui) { if(gui->group_edited >= 0) { // we get the form dt_masks_point_group_t *fpt = (dt_masks_point_group_t *)g_list_nth_data(form->points, gui->group_edited); dt_masks_form_t *sel = dt_masks_get_from_id(darktable.develop, fpt->formid); if(!sel) return 0; if(sel->type & DT_MASKS_CIRCLE) return dt_circle_events_button_released(module, pzx, pzy, which, state, sel, fpt->parentid, gui, gui->group_edited); else if(sel->type & DT_MASKS_PATH) return dt_path_events_button_released(module, pzx, pzy, which, state, sel, fpt->parentid, gui, gui->group_edited); else if(sel->type & DT_MASKS_GRADIENT) return dt_gradient_events_button_released(module, pzx, pzy, which, state, sel, fpt->parentid, gui, gui->group_edited); else if(sel->type & DT_MASKS_ELLIPSE) return dt_ellipse_events_button_released(module, pzx, pzy, which, state, sel, fpt->parentid, gui, gui->group_edited); else if(sel->type & DT_MASKS_BRUSH) return dt_brush_events_button_released(module, pzx, pzy, which, state, sel, fpt->parentid, gui, gui->group_edited); } return 0; }
static int dt_group_events_mouse_scrolled(struct dt_iop_module_t *module, float pzx, float pzy, int up, uint32_t state, dt_masks_form_t *form, dt_masks_form_gui_t *gui) { if (gui->group_edited >=0) { //we get the form dt_masks_point_group_t *fpt = (dt_masks_point_group_t *)g_list_nth_data(form->points,gui->group_edited); dt_masks_form_t *sel = dt_masks_get_from_id(darktable.develop,fpt->formid); if (!sel) return 0; if (sel->type & DT_MASKS_CIRCLE) return dt_circle_events_mouse_scrolled(module,pzx,pzy,up,state,sel,fpt->parentid,gui,gui->group_edited); else if (sel->type & DT_MASKS_PATH) return dt_path_events_mouse_scrolled(module,pzx,pzy,up,state,sel,fpt->parentid,gui,gui->group_edited); } return 0; }
static void dt_group_events_post_expose(cairo_t *cr,float zoom_scale,dt_masks_form_t *form,dt_masks_form_gui_t *gui) { GList *fpts = g_list_first(form->points); int pos = 0; while(fpts) { dt_masks_point_group_t *fpt = (dt_masks_point_group_t *) fpts->data; dt_masks_form_t *sel = dt_masks_get_from_id(darktable.develop,fpt->formid); if (sel->type & DT_MASKS_CIRCLE) dt_circle_events_post_expose(cr,zoom_scale,gui,pos); else if (sel->type & DT_MASKS_PATH) dt_path_events_post_expose(cr,zoom_scale,gui,pos,g_list_length(sel->points)); fpts = g_list_next(fpts); pos++; } }
/** gui callbacks, these are needed. */ void gui_update (dt_iop_module_t *self) { _resynch_params(self); dt_iop_spots_gui_data_t *g = (dt_iop_spots_gui_data_t *)self->gui_data; //update clones count char str[3]; dt_masks_form_t *grp = dt_masks_get_from_id(self->dev,self->blend_params->mask_id); int nb = 0; if (grp && (grp->type & DT_MASKS_GROUP)) nb = g_list_length(grp->points); snprintf(str,3,"%d",nb); gtk_label_set_text(g->label, str); //update buttons status int b1=0,b2=0; if (self->dev->form_gui && self->dev->form_visible && self->dev->form_gui->creation && self->dev->form_gui->creation_module == self) { if (self->dev->form_visible->type & DT_MASKS_CIRCLE) b1=1; else if (self->dev->form_visible->type & DT_MASKS_PATH) b2=1; } gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_circle), b1); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_path), b2); }
static void _resynch_params(struct dt_iop_module_t *self) { dt_iop_spots_params_t *p = (dt_iop_spots_params_t *)self->params; dt_develop_blend_params_t *bp = self->blend_params; // we create 2 new buffers int nid[64] = { 0 }; int nalgo[64] = { 2 }; // we go through all forms in blend params dt_masks_form_t *grp = dt_masks_get_from_id(darktable.develop, bp->mask_id); if(grp && (grp->type & DT_MASKS_GROUP)) { GList *forms = g_list_first(grp->points); int i = 0; while((i < 64) && forms) { dt_masks_point_group_t *grpt = (dt_masks_point_group_t *)forms->data; nid[i] = grpt->formid; for(int j = 0; j < 64; j++) { if(p->clone_id[j] == nid[i]) { nalgo[i] = p->clone_algo[j]; break; } } i++; forms = g_list_next(forms); } } // we reaffect params for(int i = 0; i < 64; i++) { p->clone_algo[i] = nalgo[i]; p->clone_id[i] = nid[i]; } }
static int dt_group_get_mask_roi(dt_iop_module_t *module, dt_dev_pixelpipe_iop_t *piece, dt_masks_form_t *form, const dt_iop_roi_t *roi, float *buffer) { double start2 = dt_get_wtime(); const guint nb = g_list_length(form->points); if(nb == 0) return 0; int nb_ok = 0; const int width = roi->width; const int height = roi->height; // we need to allocate a temporary buffer for intermediate creation of individual shapes float *bufs = dt_alloc_align(64, (size_t)width * height * sizeof(float)); if(bufs == NULL) return 0; // empty the output buffer memset(buffer, 0, (size_t)width * height * sizeof(float)); // and we get all masks GList *fpts = g_list_first(form->points); while(fpts) { dt_masks_point_group_t *fpt = (dt_masks_point_group_t *)fpts->data; dt_masks_form_t *sel = dt_masks_get_from_id(module->dev, fpt->formid); if(sel) { const int ok = dt_masks_get_mask_roi(module, piece, sel, roi, bufs); const float op = fpt->opacity; const int state = fpt->state; if(ok) { // first see if we need to invert this shape if(state & DT_MASKS_STATE_INVERSE) { #ifdef _OPENMP #if !defined(__SUNOS__) && !defined(__NetBSD__) #pragma omp parallel for default(none) shared(bufs) #else #pragma omp parallel for shared(bufs) #endif #endif for(int y = 0; y < height; y++) for(int x = 0; x < width; x++) { size_t index = (size_t)y * width + x; bufs[index] = 1.0f - bufs[index]; } } if(state & DT_MASKS_STATE_UNION) { #ifdef _OPENMP #if !defined(__SUNOS__) && !defined(__NetBSD__) #pragma omp parallel for default(none) shared(bufs, buffer) #else #pragma omp parallel for shared(bufs, buffer) #endif #endif for(int y = 0; y < height; y++) for(int x = 0; x < width; x++) { size_t index = (size_t)y * width + x; buffer[index] = fmaxf(buffer[index], bufs[index] * op); } } else if(state & DT_MASKS_STATE_INTERSECTION) { #ifdef _OPENMP #if !defined(__SUNOS__) && !defined(__NetBSD__) #pragma omp parallel for default(none) shared(bufs, buffer) #else #pragma omp parallel for shared(bufs, buffer) #endif #endif for(int y = 0; y < height; y++) for(int x = 0; x < width; x++) { size_t index = (size_t)y * width + x; float b1 = buffer[index]; float b2 = b2 = bufs[index]; // FIXME: is this line correct? what it supposed to be doing? if(b1 > 0.0f && b2 > 0.0f) buffer[index] = fminf(b1, b2 * op); else buffer[index] = 0.0f; } } else if(state & DT_MASKS_STATE_DIFFERENCE) { #ifdef _OPENMP #if !defined(__SUNOS__) && !defined(__NetBSD__) #pragma omp parallel for default(none) shared(bufs, buffer) #else #pragma omp parallel for shared(bufs, buffer) #endif #endif for(int y = 0; y < height; y++) for(int x = 0; x < width; x++) { size_t index = (size_t)y * width + x; float b1 = buffer[index]; float b2 = bufs[index] * op; if(b1 > 0.0f && b2 > 0.0f) buffer[index] = b1 * (1.0f - b2); } } else if(state & DT_MASKS_STATE_EXCLUSION) { #ifdef _OPENMP #if !defined(__SUNOS__) && !defined(__NetBSD__) #pragma omp parallel for default(none) shared(bufs, buffer) #else #pragma omp parallel for shared(bufs, buffer) #endif #endif for(int y = 0; y < height; y++) for(int x = 0; x < width; x++) { size_t index = (size_t)y * width + x; float b1 = buffer[index]; float b2 = bufs[index] * op; if(b1 > 0.0f && b2 > 0.0f) buffer[index] = fmaxf((1.0f - b1) * b2, b1 * (1.0f - b2)); else buffer[index] = fmaxf(b1, b2); } } else // if we are here, this mean that we just have to copy the shape and null other parts { #ifdef _OPENMP #if !defined(__SUNOS__) && !defined(__NetBSD__) #pragma omp parallel for default(none) shared(bufs, buffer) #else #pragma omp parallel for shared(bufs, buffer) #endif #endif for(int y = 0; y < height; y++) for(int x = 0; x < width; x++) { size_t index = (size_t)y * width + x; buffer[index] = bufs[index] * op; } } if(darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks %d] combine took %0.04f sec\n", nb_ok, dt_get_wtime() - start2); start2 = dt_get_wtime(); nb_ok++; } } fpts = g_list_next(fpts); } // and we free the intermediate buffer dt_free_align(bufs); return (nb_ok != 0); }
static int dt_group_get_mask(dt_iop_module_t *module, dt_dev_pixelpipe_iop_t *piece, dt_masks_form_t *form, float **buffer, int *width, int *height, int *posx, int *posy) { double start2; // we allocate buffers and values const guint nb = g_list_length(form->points); if(nb == 0) return 0; float *bufs[nb]; int w[nb]; int h[nb]; int px[nb]; int py[nb]; int ok[nb]; int states[nb]; float op[nb]; // and we get all masks GList *fpts = g_list_first(form->points); int pos = 0; int nb_ok = 0; while(fpts) { dt_masks_point_group_t *fpt = (dt_masks_point_group_t *)fpts->data; dt_masks_form_t *sel = dt_masks_get_from_id(module->dev, fpt->formid); if(sel) { ok[pos] = dt_masks_get_mask(module, piece, sel, &bufs[pos], &w[pos], &h[pos], &px[pos], &py[pos]); if(fpt->state & DT_MASKS_STATE_INVERSE) { start2 = dt_get_wtime(); _inverse_mask(module, piece, sel, &bufs[pos], &w[pos], &h[pos], &px[pos], &py[pos]); if(darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks %s] inverse took %0.04f sec\n", sel->name, dt_get_wtime() - start2); // start2 = dt_get_wtime(); } op[pos] = fpt->opacity; states[pos] = fpt->state; if(ok[pos]) nb_ok++; } fpts = g_list_next(fpts); pos++; } if(nb_ok == 0) return 0; // now we get the min, max, width, height of the final mask int l, r, t, b; l = t = INT_MAX; r = b = INT_MIN; for(int i = 0; i < nb; i++) { l = MIN(l, px[i]); t = MIN(t, py[i]); r = MAX(r, px[i] + w[i]); b = MAX(b, py[i] + h[i]); } *posx = l; *posy = t; *width = r - l; *height = b - t; // we allocate the buffer *buffer = malloc(sizeof(float) * (r - l) * (b - t)); // and we copy each buffer inside, row by row for(int i = 0; i < nb; i++) { start2 = dt_get_wtime(); if(states[i] & DT_MASKS_STATE_UNION) { for(int y = 0; y < h[i]; y++) { for(int x = 0; x < w[i]; x++) { (*buffer)[(py[i] + y - t) * (r - l) + px[i] + x - l] = fmaxf((*buffer)[(py[i] + y - t) * (r - l) + px[i] + x - l], bufs[i][y * w[i] + x] * op[i]); } } } else if(states[i] & DT_MASKS_STATE_INTERSECTION) { for(int y = 0; y < b - t; y++) { for(int x = 0; x < r - l; x++) { float b1 = (*buffer)[y * (r - l) + x]; float b2 = 0.0f; if(y + t - py[i] >= 0 && y + t - py[i] < h[i] && x + l - px[i] >= 0 && x + l - px[i] < w[i]) b2 = bufs[i][(y + t - py[i]) * w[i] + x + l - px[i]]; if(b1 > 0.0f && b2 > 0.0f) (*buffer)[y * (r - l) + x] = fminf(b1, b2 * op[i]); else (*buffer)[y * (r - l) + x] = 0.0f; } } } else if(states[i] & DT_MASKS_STATE_DIFFERENCE) { for(int y = 0; y < h[i]; y++) { for(int x = 0; x < w[i]; x++) { float b1 = (*buffer)[(py[i] + y - t) * (r - l) + px[i] + x - l]; float b2 = bufs[i][y * w[i] + x] * op[i]; if(b1 > 0.0f && b2 > 0.0f) (*buffer)[(py[i] + y - t) * (r - l) + px[i] + x - l] = b1 * (1.0f - b2); } } } else if(states[i] & DT_MASKS_STATE_EXCLUSION) { for(int y = 0; y < h[i]; y++) { for(int x = 0; x < w[i]; x++) { float b1 = (*buffer)[(py[i] + y - t) * (r - l) + px[i] + x - l]; float b2 = bufs[i][y * w[i] + x] * op[i]; if(b1 > 0.0f && b2 > 0.0f) (*buffer)[(py[i] + y - t) * (r - l) + px[i] + x - l] = fmaxf((1.0f - b1) * b2, b1 * (1.0f - b2)); else (*buffer)[(py[i] + y - t) * (r - l) + px[i] + x - l] = fmaxf((*buffer)[(py[i] + y - t) * (r - l) + px[i] + x - l], bufs[i][y * w[i] + x] * op[i]); } } } else // if we are here, this mean that we just have to copy the shape and null other parts { for(int y = 0; y < b - t; y++) { for(int x = 0; x < r - l; x++) { float b2 = 0.0f; if(y + t - py[i] >= 0 && y + t - py[i] < h[i] && x + l - px[i] >= 0 && x + l - px[i] < w[i]) b2 = bufs[i][(y + t - py[i]) * w[i] + x + l - px[i]]; (*buffer)[y * (r - l) + x] = b2 * op[i]; } } } // and we free the buffer free(bufs[i]); if(darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks %d] combine took %0.04f sec\n", i, dt_get_wtime() - start2); // start2 = dt_get_wtime(); } return 1; }
static int dt_group_events_mouse_moved(struct dt_iop_module_t *module, float pzx, float pzy, double pressure, int which, dt_masks_form_t *form, dt_masks_form_gui_t *gui) { dt_dev_zoom_t zoom = dt_control_get_dev_zoom(); int closeup = dt_control_get_dev_closeup(); float zoom_scale = dt_dev_get_zoom_scale(darktable.develop, zoom, closeup ? 2 : 1, 1); float as = 0.005f / zoom_scale * darktable.develop->preview_pipe->backbuf_width; // we first don't do anything if we are inside a scrolling session if(gui->scrollx != 0.0f && gui->scrolly != 0.0f) { float as2 = 0.015f / zoom_scale; if((gui->scrollx - pzx < as2 && gui->scrollx - pzx > -as2) && (gui->scrolly - pzy < as2 && gui->scrolly - pzy > -as2)) return 1; gui->scrollx = gui->scrolly = 0.0f; } // if a form is in edit mode, we first execute the corresponding event if(gui->group_edited >= 0) { // we get the form dt_masks_point_group_t *fpt = (dt_masks_point_group_t *)g_list_nth_data(form->points, gui->group_edited); dt_masks_form_t *sel = dt_masks_get_from_id(darktable.develop, fpt->formid); if(!sel) return 0; int rep = 0; if(sel->type & DT_MASKS_CIRCLE) rep = dt_circle_events_mouse_moved(module, pzx, pzy, pressure, which, sel, fpt->parentid, gui, gui->group_edited); else if(sel->type & DT_MASKS_PATH) rep = dt_path_events_mouse_moved(module, pzx, pzy, pressure, which, sel, fpt->parentid, gui, gui->group_edited); else if(sel->type & DT_MASKS_GRADIENT) rep = dt_gradient_events_mouse_moved(module, pzx, pzy, pressure, which, sel, fpt->parentid, gui, gui->group_edited); else if(sel->type & DT_MASKS_ELLIPSE) rep = dt_ellipse_events_mouse_moved(module, pzx, pzy, pressure, which, sel, fpt->parentid, gui, gui->group_edited); else if(sel->type & DT_MASKS_BRUSH) rep = dt_brush_events_mouse_moved(module, pzx, pzy, pressure, which, sel, fpt->parentid, gui, gui->group_edited); if(rep) return 1; // if a point is in state editing, then we don't want that another form can be selected if(gui->point_edited >= 0) return 0; } // now we check if we are near a form GList *fpts = g_list_first(form->points); int pos = 0; gui->form_selected = gui->border_selected = FALSE; gui->source_selected = gui->source_dragging = FALSE; gui->pivot_selected = FALSE; gui->feather_selected = -1; gui->point_edited = gui->point_selected = -1; gui->seg_selected = -1; gui->point_border_selected = -1; gui->group_edited = gui->group_selected = -1; while(fpts) { dt_masks_point_group_t *fpt = (dt_masks_point_group_t *)fpts->data; dt_masks_form_t *sel = dt_masks_get_from_id(darktable.develop, fpt->formid); int inside, inside_border, near, inside_source; inside = inside_border = inside_source = 0; near = -1; float xx = pzx * darktable.develop->preview_pipe->backbuf_width, yy = pzy * darktable.develop->preview_pipe->backbuf_height; if(sel->type & DT_MASKS_CIRCLE) dt_circle_get_distance(xx, yy, as, gui, pos, &inside, &inside_border, &near, &inside_source); else if(sel->type & DT_MASKS_PATH) dt_path_get_distance(xx, yy, as, gui, pos, g_list_length(sel->points), &inside, &inside_border, &near, &inside_source); else if(sel->type & DT_MASKS_GRADIENT) dt_gradient_get_distance(xx, yy, as, gui, pos, &inside, &inside_border, &near, &inside_source); else if(sel->type & DT_MASKS_ELLIPSE) dt_ellipse_get_distance(xx, yy, as, gui, pos, &inside, &inside_border, &near, &inside_source); else if(sel->type & DT_MASKS_BRUSH) dt_brush_get_distance(xx, yy, as, gui, pos, g_list_length(sel->points), &inside, &inside_border, &near, &inside_source); if(inside || inside_border || near >= 0 || inside_source) { gui->group_edited = gui->group_selected = pos; if(sel->type & DT_MASKS_CIRCLE) return dt_circle_events_mouse_moved(module, pzx, pzy, pressure, which, sel, fpt->parentid, gui, pos); else if(sel->type & DT_MASKS_PATH) return dt_path_events_mouse_moved(module, pzx, pzy, pressure, which, sel, fpt->parentid, gui, pos); else if(sel->type & DT_MASKS_GRADIENT) return dt_gradient_events_mouse_moved(module, pzx, pzy, pressure, which, sel, fpt->parentid, gui, pos); else if(sel->type & DT_MASKS_ELLIPSE) return dt_ellipse_events_mouse_moved(module, pzx, pzy, pressure, which, sel, fpt->parentid, gui, pos); else if(sel->type & DT_MASKS_BRUSH) return dt_brush_events_mouse_moved(module, pzx, pzy, pressure, which, sel, fpt->parentid, gui, pos); } fpts = g_list_next(fpts); pos++; } dt_control_queue_redraw_center(); return 0; }
void process (struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, void *i, void *o, const dt_iop_roi_t *roi_in, const dt_iop_roi_t *roi_out) { dt_iop_spots_params_t *d = (dt_iop_spots_params_t *)piece->data; dt_develop_blend_params_t *bp = self->blend_params; const int ch = piece->colors; const float *in = (float *)i; float *out = (float *)o; // we don't modify most of the image: #ifdef _OPENMP #pragma omp parallel for schedule(static) default(none) shared(out,in,roi_in,roi_out) #endif for (int k=0; k<roi_out->height; k++) { float *outb = out + ch*k*roi_out->width; const float *inb = in + ch*roi_in->width*(k+roi_out->y-roi_in->y) + ch*(roi_out->x-roi_in->x); memcpy(outb, inb, sizeof(float)*roi_out->width*ch); } // iterate through all forms dt_masks_form_t *grp = dt_masks_get_from_id(self->dev,bp->mask_id); int pos=0; if (grp && (grp->type & DT_MASKS_GROUP)) { GList *forms = g_list_first(grp->points); while(forms) { dt_masks_point_group_t *grpt = (dt_masks_point_group_t *)forms->data; //we get the spot dt_masks_form_t *form = dt_masks_get_from_id(self->dev,grpt->formid); if (!form) { forms = g_list_next(forms); pos++; continue; } //we get the area for the form int fl,ft,fw,fh; if (!dt_masks_get_area(self,piece,form,&fw,&fh,&fl,&ft)) { forms = g_list_next(forms); pos++; continue; } //if the form is outside the roi, we just skip it fw *= roi_in->scale, fh *= roi_in->scale, fl *= roi_in->scale, ft *= roi_in->scale; if (ft>=roi_out->y+roi_out->height || ft+fh<=roi_out->y || fl>=roi_out->x+roi_out->width || fl+fw<=roi_out->x) { forms = g_list_next(forms); pos++; continue; } if (d->clone_algo[pos] == 1 && (form->type & DT_MASKS_CIRCLE)) { dt_masks_point_circle_t *circle = (dt_masks_point_circle_t *)g_list_nth_data(form->points,0); // convert from world space: const int rad = circle->radius* MIN(piece->buf_in.width, piece->buf_in.height)*roi_in->scale; const int posx = (circle->center[0] * piece->buf_in.width)*roi_in->scale - rad; const int posy = (circle->center[1] * piece->buf_in.height)*roi_in->scale - rad; const int posx_source = (form->source[0]*piece->buf_in.width)*roi_in->scale - rad; const int posy_source = (form->source[1]*piece->buf_in.height)*roi_in->scale - rad; const int dx = posx-posx_source; const int dy = posy-posy_source; fw = fh = 2*rad; // convert from world space: float filter[2*rad + 1]; if(rad > 0) { for(int k=-rad; k<=rad; k++) { const float kk = 1.0f - fabsf(k/(float)rad); filter[rad + k] = kk*kk*(3.0f - 2.0f*kk); } } else { filter[0] = 1.0f; } for (int yy=posy ; yy<posy+fh; yy++) { //we test if we are inside roi_out if (yy<roi_out->y || yy>=roi_out->y+roi_out->height) continue; //we test if the source point is inside roi_in if (yy-dy<roi_in->y || yy-dy>=roi_in->y+roi_in->height) continue; for (int xx=posx ; xx<posx+fw; xx++) { //we test if we are inside roi_out if (xx<roi_out->x || xx>=roi_out->x+roi_out->width) continue; //we test if the source point is inside roi_in if (xx-dx<roi_in->x || xx-dx>=roi_in->x+roi_in->width) continue; const float f = filter[xx-posx+1]*filter[yy-posy+1]; for(int c=0; c<ch; c++) out[4*(roi_out->width*(yy-roi_out->y) + xx-roi_out->x) + c] = out[4*(roi_out->width*(yy-roi_out->y) + xx-roi_out->x) + c] * (1.0f-f) + in[4*(roi_in->width*(yy-posy+posy_source-roi_in->y) + xx-posx+posx_source-roi_in->x) + c] * f; } } } else { //we get the mask float *mask; int posx,posy,width,height; dt_masks_get_mask(self,piece,form,&mask,&width,&height,&posx,&posy); int fts = posy*roi_in->scale, fhs = height*roi_in->scale, fls = posx*roi_in->scale, fws = width*roi_in->scale; //now we search the delta with the source int dx,dy; dx=dy=0; if (form->type & DT_MASKS_PATH) { dt_masks_point_path_t *pt = (dt_masks_point_path_t *)g_list_nth_data(form->points,0); dx = pt->corner[0]*roi_in->scale*piece->buf_in.width - form->source[0]*roi_in->scale*piece->buf_in.width; dy = pt->corner[1]*roi_in->scale*piece->buf_in.height - form->source[1]*roi_in->scale*piece->buf_in.height; } else if (form->type & DT_MASKS_CIRCLE) { dt_masks_point_circle_t *pt = (dt_masks_point_circle_t *)g_list_nth_data(form->points,0); dx = pt->center[0]*roi_in->scale*piece->buf_in.width - form->source[0]*roi_in->scale*piece->buf_in.width; dy = pt->center[1]*roi_in->scale*piece->buf_in.height - form->source[1]*roi_in->scale*piece->buf_in.height; } if (dx!=0 || dy!=0) { //now we do the pixel clone for (int yy=fts+1 ; yy<fts+fhs-1; yy++) { //we test if we are inside roi_out if (yy<roi_out->y || yy>=roi_out->y+roi_out->height) continue; //we test if the source point is inside roi_in if (yy-dy<roi_in->y || yy-dy>=roi_in->y+roi_in->height) continue; for (int xx=fls+1 ; xx<fls+fws-1; xx++) { //we test if we are inside roi_out if (xx<roi_out->x || xx>=roi_out->x+roi_out->width) continue; //we test if the source point is inside roi_in if (xx-dx<roi_in->x || xx-dx>=roi_in->x+roi_in->width) continue; float f = mask[((int)((yy-fts)/roi_in->scale))*width + (int)((xx-fls)/roi_in->scale)]; //we can add the opacity here for(int c=0; c<ch; c++) out[4*(roi_out->width*(yy-roi_out->y) + xx-roi_out->x) + c] = out[4*(roi_out->width*(yy-roi_out->y) + xx-roi_out->x) + c] * (1.0f-f) + in[4*(roi_in->width*(yy-dy-roi_in->y) + xx-dx-roi_in->x) + c] * f; } } } free(mask); } pos++; forms = g_list_next(forms); } } }
static int dt_circle_events_button_released(struct dt_iop_module_t *module,float pzx, float pzy, int which, uint32_t state, dt_masks_form_t *form, int parentid, dt_masks_form_gui_t *gui,int index) { if (which == 3 && parentid > 0 && gui->edit_mode == DT_MASKS_EDIT_FULL) { dt_masks_init_formgui(darktable.develop); //we hide the form if (!(darktable.develop->form_visible->type & DT_MASKS_GROUP)) darktable.develop->form_visible = NULL; else if (g_list_length(darktable.develop->form_visible->points) < 2) darktable.develop->form_visible = NULL; else { GList *forms = g_list_first(darktable.develop->form_visible->points); while (forms) { dt_masks_point_group_t *gpt = (dt_masks_point_group_t *)forms->data; if (gpt->formid == form->formid) { darktable.develop->form_visible->points = g_list_remove(darktable.develop->form_visible->points,gpt); break; } forms = g_list_next(forms); } } //we remove the shape dt_masks_form_remove(module,dt_masks_get_from_id(darktable.develop,parentid),form); dt_dev_masks_list_remove(darktable.develop,form->formid,parentid); return 1; } if (gui->form_dragging) { //we get the circle dt_masks_point_circle_t *circle = (dt_masks_point_circle_t *) (g_list_first(form->points)->data); //we end the form dragging gui->form_dragging = FALSE; //we change the center value float wd = darktable.develop->preview_pipe->backbuf_width; float ht = darktable.develop->preview_pipe->backbuf_height; float pts[2] = {pzx*wd+gui->dx,pzy*ht+gui->dy}; dt_dev_distort_backtransform(darktable.develop,pts,1); circle->center[0] = pts[0]/darktable.develop->preview_pipe->iwidth; circle->center[1] = pts[1]/darktable.develop->preview_pipe->iheight; dt_masks_write_form(form,darktable.develop); //we recreate the form points dt_masks_gui_form_remove(form,gui,index); dt_masks_gui_form_create(form,gui,index); //we save the move dt_masks_update_image(darktable.develop); return 1; } else if (gui->source_dragging) { //we end the form dragging gui->source_dragging = FALSE; if (gui->scrollx != 0.0 || gui->scrolly != 0.0) { dt_masks_point_circle_t *circle = (dt_masks_point_circle_t *) (g_list_first(form->points)->data); form->source[0] = circle->center[0] + circle->radius; form->source[1] = circle->center[1] - circle->radius; gui->scrollx = gui->scrolly = 0.0; } else { //we change the center value float wd = darktable.develop->preview_pipe->backbuf_width; float ht = darktable.develop->preview_pipe->backbuf_height; float pts[2] = {pzx*wd+gui->dx,pzy*ht+gui->dy}; dt_dev_distort_backtransform(darktable.develop,pts,1); form->source[0] = pts[0]/darktable.develop->preview_pipe->iwidth; form->source[1] = pts[1]/darktable.develop->preview_pipe->iheight; } dt_masks_write_form(form,darktable.develop); //we recreate the form points dt_masks_gui_form_remove(form,gui,index); dt_masks_gui_form_create(form,gui,index); //we save the move dt_masks_update_image(darktable.develop); return 1; } return 0; }
static int dt_ellipse_events_button_released(struct dt_iop_module_t *module, float pzx, float pzy, int which, uint32_t state, dt_masks_form_t *form, int parentid, dt_masks_form_gui_t *gui, int index) { if(which == 3 && parentid > 0 && gui->edit_mode == DT_MASKS_EDIT_FULL) { // we hide the form if(!(darktable.develop->form_visible->type & DT_MASKS_GROUP)) dt_masks_change_form_gui(NULL); else if(g_list_length(darktable.develop->form_visible->points) < 2) dt_masks_change_form_gui(NULL); else { dt_masks_clear_form_gui(darktable.develop); GList *forms = g_list_first(darktable.develop->form_visible->points); while(forms) { dt_masks_point_group_t *gpt = (dt_masks_point_group_t *)forms->data; if(gpt->formid == form->formid) { darktable.develop->form_visible->points = g_list_remove(darktable.develop->form_visible->points, gpt); free(gpt); break; } forms = g_list_next(forms); } gui->edit_mode = DT_MASKS_EDIT_FULL; } // we remove the shape dt_dev_masks_list_remove(darktable.develop, form->formid, parentid); dt_masks_form_remove(module, dt_masks_get_from_id(darktable.develop, parentid), form); return 1; } if(gui->form_dragging) { // we get the ellipse dt_masks_point_ellipse_t *ellipse = (dt_masks_point_ellipse_t *)(g_list_first(form->points)->data); // we end the form dragging gui->form_dragging = FALSE; // we change the center value float wd = darktable.develop->preview_pipe->backbuf_width; float ht = darktable.develop->preview_pipe->backbuf_height; float pts[2] = { pzx * wd + gui->dx, pzy * ht + gui->dy }; dt_dev_distort_backtransform(darktable.develop, pts, 1); ellipse->center[0] = pts[0] / darktable.develop->preview_pipe->iwidth; ellipse->center[1] = pts[1] / darktable.develop->preview_pipe->iheight; dt_masks_write_form(form, darktable.develop); // we recreate the form points dt_masks_gui_form_remove(form, gui, index); dt_masks_gui_form_create(form, gui, index); // we save the move dt_masks_update_image(darktable.develop); return 1; } else if(gui->border_toggling) { // we get the ellipse dt_masks_point_ellipse_t *ellipse = (dt_masks_point_ellipse_t *)(g_list_first(form->points)->data); // we end the border toggling gui->border_toggling = FALSE; // toggle feathering type of border and adjust border radius accordingly if(ellipse->flags & DT_MASKS_ELLIPSE_PROPORTIONAL) { const float min_radius = fmin(ellipse->radius[0], ellipse->radius[1]); ellipse->border = ellipse->border * min_radius; ellipse->border = CLAMP(ellipse->border, 0.001f, 1.0f); ellipse->flags &= ~DT_MASKS_ELLIPSE_PROPORTIONAL; } else { const float min_radius = fmin(ellipse->radius[0], ellipse->radius[1]); ellipse->border = ellipse->border/min_radius; ellipse->border = CLAMP(ellipse->border, 0.001f/min_radius, 1.0f/min_radius); ellipse->flags |= DT_MASKS_ELLIPSE_PROPORTIONAL; } if(form->type & DT_MASKS_CLONE) { dt_conf_set_int("plugins/darkroom/spots/ellipse_flags", ellipse->flags); dt_conf_set_float("plugins/darkroom/spots/ellipse_border", ellipse->border); } else { dt_conf_set_int("plugins/darkroom/masks/ellipse/flags", ellipse->flags); dt_conf_set_float("plugins/darkroom/masks/ellipse/border", ellipse->border); } dt_masks_write_form(form, darktable.develop); // we recreate the form points dt_masks_gui_form_remove(form, gui, index); dt_masks_gui_form_create(form, gui, index); // we save the new parameters dt_masks_update_image(darktable.develop); return 1; } else if(gui->form_rotating && gui->edit_mode == DT_MASKS_EDIT_FULL) { // we get the ellipse dt_masks_point_ellipse_t *ellipse = (dt_masks_point_ellipse_t *)(g_list_first(form->points)->data); // we end the form rotating gui->form_rotating = FALSE; float wd = darktable.develop->preview_pipe->backbuf_width; float ht = darktable.develop->preview_pipe->backbuf_height; float x = pzx * wd; float y = pzy * ht; // we need the reference point dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index); if(!gpt) return 0; float xref = gpt->points[0]; float yref = gpt->points[1]; float dv = atan2(y - yref, x - xref) - atan2(-gui->dy, -gui->dx); ellipse->rotation += dv / M_PI * 180.0f; ellipse->rotation = fmodf(ellipse->rotation, 360.0f); if(form->type & DT_MASKS_CLONE) dt_conf_set_float("plugins/darkroom/spots/ellipse_rotation", ellipse->rotation); else dt_conf_set_float("plugins/darkroom/masks/ellipse/rotation", ellipse->rotation); dt_masks_write_form(form, darktable.develop); // we recreate the form points dt_masks_gui_form_remove(form, gui, index); dt_masks_gui_form_create(form, gui, index); // we save the rotation dt_masks_update_image(darktable.develop); return 1; } else if(gui->point_dragging >= 1 && gui->edit_mode == DT_MASKS_EDIT_FULL) { // we get the ellipse dt_masks_point_ellipse_t *ellipse = (dt_masks_point_ellipse_t *)(g_list_first(form->points)->data); const int k = gui->point_dragging; // we end the point dragging gui->point_dragging = -1; // we need the reference points dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index); if(!gpt) return 0; const float xref = gpt->points[0]; const float yref = gpt->points[1]; const float rx = gpt->points[k * 2] - xref; const float ry = gpt->points[k * 2 + 1] - yref; const float deltax = gui->posx + gui->dx - xref; const float deltay = gui->posy + gui->dy - yref; const float r = sqrtf(rx * rx + ry * ry); const float d = (rx * deltax + ry * deltay) / r; const float s = fmaxf(r > 0.0f ? (r + d) / r : 0.0f, 0.0f); // make sure we adjust the right radius: anchor points and 1 and 2 correspond to the ellipse's longer axis if(((k == 1 || k == 2) && ellipse->radius[0] > ellipse->radius[1]) || ((k == 3 || k == 4) && ellipse->radius[0] <= ellipse->radius[1])) { ellipse->radius[0] = MAX(0.002f, ellipse->radius[0] * s); if(form->type & DT_MASKS_CLONE) dt_conf_set_float("plugins/darkroom/spots/ellipse_radius_a", ellipse->radius[0]); else dt_conf_set_float("plugins/darkroom/masks/ellipse/radius_a", ellipse->radius[0]); } else { ellipse->radius[1] = MAX(0.002f, ellipse->radius[1] * s); if(form->type & DT_MASKS_CLONE) dt_conf_set_float("plugins/darkroom/spots/ellipse_radius_b", ellipse->radius[1]); else dt_conf_set_float("plugins/darkroom/masks/ellipse/radius_b", ellipse->radius[1]); } dt_masks_write_form(form, darktable.develop); // we recreate the form points dt_masks_gui_form_remove(form, gui, index); dt_masks_gui_form_create(form, gui, index); // we save the rotation dt_masks_update_image(darktable.develop); return 1; } else if(gui->source_dragging) { // we end the form dragging gui->source_dragging = FALSE; if(gui->scrollx != 0.0 || gui->scrolly != 0.0) { dt_masks_point_ellipse_t *ellipse = (dt_masks_point_ellipse_t *)(g_list_first(form->points)->data); form->source[0] = ellipse->center[0] + ellipse->radius[0]; form->source[1] = ellipse->center[1] - ellipse->radius[1]; gui->scrollx = gui->scrolly = 0.0; } else { // we change the center value float wd = darktable.develop->preview_pipe->backbuf_width; float ht = darktable.develop->preview_pipe->backbuf_height; float pts[2] = { pzx * wd + gui->dx, pzy * ht + gui->dy }; dt_dev_distort_backtransform(darktable.develop, pts, 1); form->source[0] = pts[0] / darktable.develop->preview_pipe->iwidth; form->source[1] = pts[1] / darktable.develop->preview_pipe->iheight; } dt_masks_write_form(form, darktable.develop); // we recreate the form points dt_masks_gui_form_remove(form, gui, index); dt_masks_gui_form_create(form, gui, index); // we save the move dt_masks_update_image(darktable.develop); return 1; } return 0; }
static int dt_gradient_events_button_released(struct dt_iop_module_t *module,float pzx, float pzy, int which, uint32_t state, dt_masks_form_t *form, int parentid, dt_masks_form_gui_t *gui, int index) { if (which == 3 && parentid > 0 && gui->edit_mode == DT_MASKS_EDIT_FULL) { dt_masks_clear_form_gui(darktable.develop); //we hide the form if (!(darktable.develop->form_visible->type & DT_MASKS_GROUP)) darktable.develop->form_visible = NULL; else if (g_list_length(darktable.develop->form_visible->points) < 2) darktable.develop->form_visible = NULL; else { GList *forms = g_list_first(darktable.develop->form_visible->points); while (forms) { dt_masks_point_group_t *gpt = (dt_masks_point_group_t *)forms->data; if (gpt->formid == form->formid) { darktable.develop->form_visible->points = g_list_remove(darktable.develop->form_visible->points,gpt); break; } forms = g_list_next(forms); } } //we remove the shape dt_masks_form_remove(module,dt_masks_get_from_id(darktable.develop,parentid),form); dt_dev_masks_list_remove(darktable.develop,form->formid,parentid); return 1; } if(gui->form_dragging && gui->edit_mode == DT_MASKS_EDIT_FULL) { //we get the gradient dt_masks_point_gradient_t *gradient = (dt_masks_point_gradient_t *) (g_list_first(form->points)->data); //we end the form dragging gui->form_dragging = FALSE; //we change the center value float wd = darktable.develop->preview_pipe->backbuf_width; float ht = darktable.develop->preview_pipe->backbuf_height; float pts[2] = {pzx*wd+gui->dx,pzy*ht+gui->dy}; dt_dev_distort_backtransform(darktable.develop,pts,1); gradient->anchor[0] = pts[0]/darktable.develop->preview_pipe->iwidth; gradient->anchor[1] = pts[1]/darktable.develop->preview_pipe->iheight; dt_masks_write_form(form,darktable.develop); //we recreate the form points dt_masks_gui_form_remove(form,gui,index); dt_masks_gui_form_create(form,gui,index); //we save the move dt_masks_update_image(darktable.develop); return 1; } else if(gui->form_rotating && gui->edit_mode == DT_MASKS_EDIT_FULL) { //we get the gradient dt_masks_point_gradient_t *gradient = (dt_masks_point_gradient_t *) (g_list_first(form->points)->data); //we end the form rotating gui->form_rotating = FALSE; float wd = darktable.develop->preview_pipe->backbuf_width; float ht = darktable.develop->preview_pipe->backbuf_height; float x = pzx*wd; float y = pzy*ht; //we need the reference point dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *) g_list_nth_data(gui->points,index); if (!gpt) return 0; float xref = gpt->points[0]; float yref = gpt->points[1]; float dv = atan2(y - yref, x - xref) - atan2(-gui->dy, -gui->dx); gradient->rotation -= dv/M_PI*180.0f; dt_masks_write_form(form,darktable.develop); //we recreate the form points dt_masks_gui_form_remove(form,gui,index); dt_masks_gui_form_create(form,gui,index); //we save the rotation dt_masks_update_image(darktable.develop); return 1; } return 0; }
static int dt_circle_events_button_pressed(struct dt_iop_module_t *module, float pzx, float pzy, double pressure, int which, int type, uint32_t state, dt_masks_form_t *form, int parentid, dt_masks_form_gui_t *gui, int index) { if(!gui) return 0; if(gui->source_selected && !gui->creation && gui->edit_mode == DT_MASKS_EDIT_FULL) { dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index); if(!gpt) return 0; // we start the form dragging gui->source_dragging = TRUE; gui->dx = gpt->source[0] - gui->posx; gui->dy = gpt->source[1] - gui->posy; return 1; } else if(gui->form_selected && !gui->creation && gui->edit_mode == DT_MASKS_EDIT_FULL) { dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index); if(!gpt) return 0; // we start the form dragging gui->form_dragging = TRUE; gui->dx = gpt->points[0] - gui->posx; gui->dy = gpt->points[1] - gui->posy; return 1; } else if(gui->creation && (which == 3)) { gui->creation_continuous = FALSE; gui->creation_continuous_module = NULL; dt_masks_set_edit_mode(module, DT_MASKS_EDIT_FULL); dt_masks_iop_update(module); dt_control_queue_redraw_center(); return 1; } else if(gui->creation && which == 1 && (((state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) || ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK))) { // set some absolute or relative position for the source of the clone mask if(form->type & DT_MASKS_CLONE) dt_masks_set_source_pos_initial_state(gui, state, pzx, pzy); return 1; } else if(gui->creation) { dt_iop_module_t *crea_module = gui->creation_module; // we create the circle dt_masks_point_circle_t *circle = (dt_masks_point_circle_t *)(malloc(sizeof(dt_masks_point_circle_t))); // we change the center value float wd = darktable.develop->preview_pipe->backbuf_width; float ht = darktable.develop->preview_pipe->backbuf_height; float pts[2] = { pzx * wd, pzy * ht }; dt_dev_distort_backtransform(darktable.develop, pts, 1); circle->center[0] = pts[0] / darktable.develop->preview_pipe->iwidth; circle->center[1] = pts[1] / darktable.develop->preview_pipe->iheight; if(form->type & (DT_MASKS_CLONE|DT_MASKS_NON_CLONE)) { circle->radius = dt_conf_get_sanitize_set("plugins/darkroom/spots/circle_size", 0.001f, 0.5f); circle->border = dt_conf_get_sanitize_set("plugins/darkroom/spots/circle_border", 0.0005f, 0.5f); // calculate the source position if(form->type & DT_MASKS_CLONE) { dt_masks_set_source_pos_initial_value(gui, DT_MASKS_CIRCLE, form, pzx, pzy); } else { // not used by regular masks form->source[0] = form->source[1] = 0.0f; } } else { circle->radius = dt_conf_get_sanitize_set("plugins/darkroom/masks/circle/size", 0.001f, 0.5f); circle->border = dt_conf_get_sanitize_set("plugins/darkroom/masks/circle/border", 0.0005f, 0.5f); // not used for masks form->source[0] = form->source[1] = 0.0f; } form->points = g_list_append(form->points, circle); dt_masks_gui_form_save_creation(darktable.develop, crea_module, form, gui); if(crea_module) { // we save the move dt_dev_add_history_item(darktable.develop, crea_module, TRUE); // and we switch in edit mode to show all the forms if(gui->creation_continuous) dt_masks_set_edit_mode_single_form(crea_module, form->formid, DT_MASKS_EDIT_FULL); else dt_masks_set_edit_mode(crea_module, DT_MASKS_EDIT_FULL); dt_masks_iop_update(crea_module); gui->creation_module = NULL; } else { // we select the new form dt_dev_masks_selection_change(darktable.develop, form->formid, TRUE); } // if we draw a clone circle, we start now the source dragging if(form->type & (DT_MASKS_CLONE|DT_MASKS_NON_CLONE)) { dt_masks_form_t *grp = darktable.develop->form_visible; if(!grp || !(grp->type & DT_MASKS_GROUP)) return 1; int pos3 = 0, pos2 = -1; GList *fs = g_list_first(grp->points); while(fs) { dt_masks_point_group_t *pt = (dt_masks_point_group_t *)fs->data; if(pt->formid == form->formid) { pos2 = pos3; break; } pos3++; fs = g_list_next(fs); } if(pos2 < 0) return 1; dt_masks_form_gui_t *gui2 = darktable.develop->form_gui; if(!gui2) return 1; if(form->type & DT_MASKS_CLONE) gui2->source_dragging = TRUE; else gui2->form_dragging = TRUE; gui2->group_edited = gui2->group_selected = pos2; gui2->posx = pzx * darktable.develop->preview_pipe->backbuf_width; gui2->posy = pzy * darktable.develop->preview_pipe->backbuf_height; gui2->dx = 0.0; gui2->dy = 0.0; gui2->scrollx = pzx; gui2->scrolly = pzy; gui2->form_selected = TRUE; // we also want to be selected after button released dt_masks_select_form(module, dt_masks_get_from_id(darktable.develop, form->formid)); } return 1; } return 0; }