int dt_masks_group_render(dt_iop_module_t *module, dt_dev_pixelpipe_iop_t *piece, dt_masks_form_t *form, float **buffer, int *roi, float scale) { double start2 = dt_get_wtime(); if(!form) return 0; float *mask = *buffer; // we first reset the buffer to 0 memset(mask, 0, roi[2] * roi[3] * sizeof(float)); // we get the mask float *fm = NULL; int fx = roi[0], fy = roi[1], fw = roi[2], fh = roi[3]; if(!dt_masks_get_mask(module, piece, form, &fm, &fw, &fh, &fx, &fy)) return 0; if(darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks] get all masks took %0.04f sec\n", dt_get_wtime() - start2); start2 = dt_get_wtime(); // we don't want row which are outside the roi_out int fxx = fx * scale; int fww = fw * scale; int fyy = fy * scale; int fhh = fh * scale; if(fxx > roi[0] + roi[2]) { free(fm); return 1; } if(fxx < roi[0]) fww += fxx - roi[0], fxx = roi[0]; if(fww + fxx >= roi[0] + roi[2]) fww = roi[0] + roi[2] - fxx - 1; // we adjust to avoid rounding errors if(fyy / scale - fy < 0) fyy++, fhh--; if(fxx / scale - fx < 0) fxx++, fww--; if((fyy + fhh) / scale - fy >= fh) fhh--; if((fxx + fww) / scale - fx >= fw) fww--; // we apply the mask row by row for(int yy = fyy; yy < fyy + fhh; yy++) { if(yy < roi[1] || yy >= roi[1] + roi[3]) continue; for(int xx = fxx; xx < fxx + fww; xx++) { int a = (yy / scale - fy); int b = (xx / scale); mask[(yy - roi[1]) * roi[2] + xx - roi[0]] = fmaxf(mask[(yy - roi[1]) * roi[2] + xx - roi[0]], fm[a * fw + b - fx]); } } // we free the mask free(fm); if(darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks] scale all masks took %0.04f sec\n", dt_get_wtime() - start2); return 1; }
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; }
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); } } }