Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
src/iop/drawlayer/cache.c
Go to the documentation of this file.
1/*
2 This file is part of the Ansel project.
3 Copyright (C) 2026 Aurélien PIERRE.
4
5 Ansel is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 Ansel is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with Ansel. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19#include "common/darktable.h"
20#include "iop/drawlayer/cache.h"
21
23
24#include <math.h>
25#include <string.h>
26
28void *dt_drawlayer_cache_alloc_temp_buffer(const size_t bytes, const char *name)
29{
30 if(bytes == 0) return NULL;
32}
33
35void dt_drawlayer_cache_free_temp_buffer(void **buffer, const char *name)
36{
37 if(IS_NULL_PTR(buffer) || !*buffer) return;
39}
40
42float *dt_drawlayer_cache_ensure_scratch_buffer(float **buffer, size_t *capacity_pixels, const size_t needed_pixels,
43 const char *name)
44{
45 if(IS_NULL_PTR(buffer) || IS_NULL_PTR(capacity_pixels) || needed_pixels == 0) return NULL;
46 if(*capacity_pixels < needed_pixels)
47 {
49 float *new_buffer = dt_drawlayer_cache_alloc_temp_buffer(needed_pixels * 4 * sizeof(float), name);
50 if(IS_NULL_PTR(new_buffer)) return NULL;
51 *buffer = new_buffer;
52 *capacity_pixels = needed_pixels;
53 }
54 return *buffer;
55}
56
58void dt_drawlayer_cache_clear_transparent_float(float *pixels, const size_t pixel_count)
59{
60 if(IS_NULL_PTR(pixels)) return;
61 memset(pixels, 0, pixel_count * 4 * sizeof(float));
62}
63
65void dt_drawlayer_cache_patch_clear(dt_drawlayer_cache_patch_t *patch, const char *external_alloc_name)
66{
67 if(IS_NULL_PTR(patch)) return;
68 if(patch->external_alloc)
69 {
70 if(patch->cache_entry)
72 void *buffer = patch->pixels;
74 }
75 else if(patch->cache_entry)
76 {
78 }
79 else
80 {
81 dt_free(patch->pixels);
82 }
83 memset(patch, 0, sizeof(*patch));
84}
85
88 const size_t pixel_count, const int width, const int height,
89 const char *name, int *created_out)
90{
91 if(IS_NULL_PTR(patch) || pixel_count == 0 || width <= 0 || height <= 0) return FALSE;
92
93 void *data = NULL;
94 dt_pixel_cache_entry_t *entry = NULL;
95 const int created = dt_dev_pixelpipe_cache_get(darktable.pixelpipe_cache, hash, pixel_count * 4 * sizeof(float),
96 name, DT_DEV_PIXELPIPE_NONE, TRUE, &data, &entry);
97 if(!IS_NULL_PTR(created_out)) *created_out = created;
98 if(IS_NULL_PTR(data) || IS_NULL_PTR(entry))
99 {
100 if(entry)
101 {
104 }
105 return FALSE;
106 }
107
108 dt_drawlayer_cache_patch_clear(patch, "drawlayer patch");
109 patch->x = 0;
110 patch->y = 0;
111 patch->width = width;
112 patch->height = height;
113 patch->pixels = (float *)data;
114 patch->cache_entry = entry;
115 patch->cache_hash = hash;
116 patch->external_alloc = FALSE;
117
119 return TRUE;
120}
121
124 const int height, const char *name)
125{
126 if(IS_NULL_PTR(mask) || width <= 0 || height <= 0) return FALSE;
127
128 const gboolean size_changed = (mask->width != width || mask->height != height || !mask->pixels);
129 if(size_changed)
130 {
132 mask->width = width;
133 mask->height = height;
134 mask->x = 0;
135 mask->y = 0;
136 mask->pixels = dt_drawlayer_cache_alloc_temp_buffer((size_t)width * height * sizeof(float), name);
137 mask->external_alloc = TRUE;
138 if(IS_NULL_PTR(mask->pixels))
139 {
140 mask->width = 0;
141 mask->height = 0;
142 return FALSE;
143 }
144
146 mask->cache_hash = mask->cache_entry ? mask->cache_entry->hash : DT_PIXELPIPE_CACHE_HASH_INVALID;
147 if(IS_NULL_PTR(mask->cache_entry))
148 {
150 return FALSE;
151 }
152 }
153
154 memset(mask->pixels, 0, (size_t)width * height * sizeof(float));
155 return TRUE;
156}
157
164
171
178
185
187void dt_drawlayer_cache_invalidate_process_patch_state(gboolean *process_patch_valid, gboolean *process_patch_dirty,
188 dt_drawlayer_damaged_rect_t *process_dirty_rect,
189 int *process_patch_padding,
190 dt_iop_roi_t *process_combined_roi)
191{
192 if(process_patch_valid) *process_patch_valid = FALSE;
193 if(process_patch_dirty) *process_patch_dirty = FALSE;
194 if(!IS_NULL_PTR(process_dirty_rect)) dt_drawlayer_paint_runtime_state_reset(process_dirty_rect);
195 if(!IS_NULL_PTR(process_patch_padding)) *process_patch_padding = 0;
196 if(!IS_NULL_PTR(process_combined_roi)) memset(process_combined_roi, 0, sizeof(*process_combined_roi));
197}
198
201 dt_drawlayer_cache_patch_t *process_stroke_mask,
202 const int width,
203 const int height, const char *patch_buffer_name,
204 const char *mask_buffer_name)
205{
206 if(IS_NULL_PTR(process_patch) || !process_stroke_mask || width <= 0 || height <= 0)
207 return FALSE;
208
209 const gboolean size_changed = (process_patch->width != width || process_patch->height != height
210 || !process_patch->pixels);
211 if(size_changed)
212 {
213 dt_drawlayer_cache_patch_clear(process_patch, patch_buffer_name);
214 process_patch->width = width;
215 process_patch->height = height;
216 process_patch->x = 0;
217 process_patch->y = 0;
218 process_patch->pixels = dt_drawlayer_cache_alloc_temp_buffer((gsize)width * height * 4 * sizeof(float),
219 patch_buffer_name);
220 process_patch->external_alloc = TRUE;
221 if(IS_NULL_PTR(process_patch->pixels)) return FALSE;
222 process_patch->cache_entry
224 process_patch->cache_hash = process_patch->cache_entry ? process_patch->cache_entry->hash
226 if(IS_NULL_PTR(process_patch->cache_entry)) return FALSE;
227 }
228
229 const size_t mask_count = (size_t)width * height;
230 if(size_changed || process_stroke_mask->width != width || process_stroke_mask->height != height
231 || !process_stroke_mask->pixels)
232 {
233 dt_drawlayer_cache_patch_clear(process_stroke_mask, mask_buffer_name);
234 process_stroke_mask->width = width;
235 process_stroke_mask->height = height;
236 process_stroke_mask->x = 0;
237 process_stroke_mask->y = 0;
238 process_stroke_mask->pixels = dt_drawlayer_cache_alloc_temp_buffer(mask_count * sizeof(float),
239 mask_buffer_name);
240 process_stroke_mask->external_alloc = TRUE;
241 if(!process_stroke_mask->pixels)
242 {
243 process_stroke_mask->width = 0;
244 process_stroke_mask->height = 0;
245 return FALSE;
246 }
247 process_stroke_mask->cache_entry
249 process_stroke_mask->cache_hash = process_stroke_mask->cache_entry ? process_stroke_mask->cache_entry->hash
251 if(!process_stroke_mask->cache_entry)
252 {
253 process_stroke_mask->width = 0;
254 process_stroke_mask->height = 0;
255 return FALSE;
256 }
257 }
258
259 return TRUE;
260}
261
264 const dt_iop_roi_t *process_roi, const int current_full_w,
265 const int current_full_h, const int src_w, const int src_h,
266 const int module_origin_x, const int module_origin_y,
267 dt_iop_roi_t *combined_roi)
268{
269 if(IS_NULL_PTR(piece) || IS_NULL_PTR(process_roi) || IS_NULL_PTR(combined_roi) || current_full_w <= 0 || current_full_h <= 0
270 || src_w <= 0 || src_h <= 0)
271 {
272 if(!IS_NULL_PTR(combined_roi)) memset(combined_roi, 0, sizeof(*combined_roi));
273 return;
274 }
275
276 const float fit = fminf((float)current_full_w / (float)src_w, (float)current_full_h / (float)src_h);
277 const int scaled_w = MAX(1, MIN(current_full_w, (int)lroundf(src_w * fit)));
278 const int scaled_h = MAX(1, MIN(current_full_h, (int)lroundf(src_h * fit)));
279 const int fit_offset_x = MAX((current_full_w - scaled_w) / 2, 0);
280 const int fit_offset_y = MAX((current_full_h - scaled_h) / 2, 0);
281
282 const float inv_scale = process_roi->scale > 1e-6f ? (1.0f / process_roi->scale) : 0.0f;
283 const float tile_origin_canvas_x = (float)module_origin_x + process_roi->x * inv_scale;
284 const float tile_origin_canvas_y = (float)module_origin_y + process_roi->y * inv_scale;
285
286 combined_roi->x = (int)lroundf((tile_origin_canvas_x - fit_offset_x) * process_roi->scale);
287 combined_roi->y = (int)lroundf((tile_origin_canvas_y - fit_offset_y) * process_roi->scale);
288 combined_roi->width = process_roi->width;
289 combined_roi->height = process_roi->height;
290 combined_roi->scale = process_roi->scale * fmaxf(fit, 1e-6f);
291}
292
295 const int current_full_w, const int current_full_h,
296 int *module_origin_x, int *module_origin_y)
297{
298 int origin_x = 0;
299 int origin_y = 0;
300
301 if(!IS_NULL_PTR(piece))
302 {
303 origin_x = piece->buf_in.x;
304 origin_y = piece->buf_in.y;
305
306 if(origin_x == 0 && piece->buf_in.width < current_full_w && piece->buf_in.height == current_full_h)
307 origin_x = MAX((current_full_w - piece->buf_in.width) / 2, 0);
308 if(origin_y == 0 && piece->buf_in.height < current_full_h && piece->buf_in.width == current_full_w)
309 origin_y = MAX((current_full_h - piece->buf_in.height) / 2, 0);
310 }
311
312 if(module_origin_x) *module_origin_x = origin_x;
313 if(module_origin_y) *module_origin_y = origin_y;
314}
315
318 const dt_iop_roi_t *process_roi,
319 const int current_full_w,
320 const int current_full_h,
321 const int src_w, const int src_h,
322 dt_iop_roi_t *combined_roi)
323{
324 if(IS_NULL_PTR(piece) || IS_NULL_PTR(process_roi) || IS_NULL_PTR(combined_roi) || current_full_w <= 0 || current_full_h <= 0
325 || src_w <= 0 || src_h <= 0)
326 {
327 if(!IS_NULL_PTR(combined_roi)) memset(combined_roi, 0, sizeof(*combined_roi));
328 return;
329 }
330
331 int module_origin_x = 0;
332 int module_origin_y = 0;
333 dt_drawlayer_cache_resolve_piece_input_origin(piece, current_full_w, current_full_h,
334 &module_origin_x, &module_origin_y);
335
336 dt_drawlayer_cache_build_combined_process_roi(piece, process_roi, current_full_w, current_full_h,
337 src_w, src_h, module_origin_x, module_origin_y, combined_roi);
338}
339
342 const int process_patch_padding,
343 const dt_iop_roi_t *roi_out,
344 dt_iop_roi_t *blend_target_roi,
345 dt_iop_roi_t *source_process_roi,
346 gboolean *direct_copy)
347{
348 if(IS_NULL_PTR(process_patch) || IS_NULL_PTR(roi_out) || IS_NULL_PTR(direct_copy)) return FALSE;
349 if(process_patch->width <= 0 || process_patch->height <= 0 || roi_out->width <= 0 || roi_out->height <= 0)
350 return FALSE;
351
352 if(!IS_NULL_PTR(source_process_roi))
353 {
354 source_process_roi->x = 0;
355 source_process_roi->y = 0;
356 source_process_roi->width = process_patch->width;
357 source_process_roi->height = process_patch->height;
358 source_process_roi->scale = 1.0f;
359 }
360
361 const int process_pad = MAX(process_patch_padding, 0);
362 if(!IS_NULL_PTR(blend_target_roi))
363 {
364 blend_target_roi->x = process_pad;
365 blend_target_roi->y = process_pad;
366 blend_target_roi->width = roi_out->width;
367 blend_target_roi->height = roi_out->height;
368 blend_target_roi->scale = 1.0f;
369 }
370
371 *direct_copy = (process_patch->width == roi_out->width && process_patch->height == roi_out->height);
372 return TRUE;
373}
374
377 const int process_patch_padding,
378 const dt_iop_roi_t *roi_out,
379 float *layerbuf,
380 const int layerbuf_width)
381{
382 if(IS_NULL_PTR(process_patch) || IS_NULL_PTR(roi_out) || IS_NULL_PTR(layerbuf) || IS_NULL_PTR(process_patch->pixels) || layerbuf_width <= 0) return FALSE;
383
384 dt_iop_roi_t target_roi = { 0 };
385 dt_iop_roi_t source_process_roi = { 0 };
386 gboolean direct_copy = FALSE;
387 if(!dt_drawlayer_cache_build_process_blend_rois(process_patch, process_patch_padding, roi_out,
388 &target_roi, &source_process_roi, &direct_copy))
389 return FALSE;
390
391 if(direct_copy)
392 {
393 memcpy(layerbuf, process_patch->pixels, (size_t)roi_out->width * roi_out->height * 4 * sizeof(float));
394 return TRUE;
395 }
396
397 dt_iop_clip_and_zoom(layerbuf, process_patch->pixels, &target_roi, &source_process_roi,
398 layerbuf_width, process_patch->width);
399 return TRUE;
400}
401
404 const dt_drawlayer_cache_patch_t *base_stroke_mask,
405 dt_drawlayer_cache_patch_t *process_patch,
406 dt_drawlayer_cache_patch_t *process_stroke_mask,
407 const dt_iop_roi_t *combined_roi, const int process_pad,
408 const int patch_width, const int patch_height,
409 gboolean *process_patch_valid,
410 gboolean *process_patch_dirty,
411 dt_drawlayer_damaged_rect_t *process_dirty_rect,
412 int *process_patch_padding,
413 dt_iop_roi_t *process_combined_roi,
414 const char *patch_buffer_name,
415 const char *mask_buffer_name)
416{
417 if(IS_NULL_PTR(base_patch) || IS_NULL_PTR(process_patch) || IS_NULL_PTR(combined_roi) || IS_NULL_PTR(base_patch->pixels) || base_patch->width <= 0
418 || base_patch->height <= 0 || patch_width <= 0 || patch_height <= 0 || combined_roi->scale <= 1e-6f)
419 return FALSE;
420
421 process_patch->x = 0;
422 process_patch->y = 0;
423 if(!IS_NULL_PTR(process_patch_padding)) *process_patch_padding = process_pad;
424 if(!IS_NULL_PTR(process_combined_roi)) *process_combined_roi = *combined_roi;
425
427 if(fabs(combined_roi->scale - 1.0) <= 1e-6f)
428 {
429 const int src_x0 = MAX(0, combined_roi->x);
430 const int src_y0 = MAX(0, combined_roi->y);
431 const int src_x1 = MIN(base_patch->width, combined_roi->x + combined_roi->width);
432 const int src_y1 = MIN(base_patch->height, combined_roi->y + combined_roi->height);
433 const int copy_w = src_x1 - src_x0;
434 const int copy_h = src_y1 - src_y0;
435
436 if(copy_w > 0 && copy_h > 0)
437 {
438 const int dst_x0 = src_x0 - combined_roi->x;
439 const int dst_y0 = src_y0 - combined_roi->y;
440 const gboolean full_coverage = (dst_x0 == 0 && dst_y0 == 0
441 && copy_w == process_patch->width
442 && copy_h == process_patch->height);
443 if(!full_coverage)
444 {
445 memset(process_patch->pixels, 0,
446 (size_t)process_patch->width * process_patch->height * 4 * sizeof(float));
447 }
448 __OMP_PARALLEL_FOR__(if(copy_h > 8))
449 for(int y = 0; y < copy_h; y++)
450 {
451 const float *src = base_patch->pixels + 4 * ((size_t)(src_y0 + y) * base_patch->width + src_x0);
452 float *dst = process_patch->pixels + 4 * ((size_t)(dst_y0 + y) * process_patch->width + dst_x0);
453 memcpy(dst, src, (size_t)copy_w * 4 * sizeof(float));
454 }
455 }
456 else
457 {
458 memset(process_patch->pixels, 0, (size_t)process_patch->width * process_patch->height * 4 * sizeof(float));
459 }
460 }
461 else
462 {
463 const dt_iop_roi_t source_full_roi = {
464 .x = 0,
465 .y = 0,
466 .width = base_patch->width,
467 .height = base_patch->height,
468 .scale = 1.0f,
469 };
470 dt_iop_clip_and_zoom(process_patch->pixels, base_patch->pixels, combined_roi, &source_full_roi,
471 process_patch->width, base_patch->width);
472 }
474
475 if(process_patch_valid) *process_patch_valid = TRUE;
476 if(process_patch_dirty) *process_patch_dirty = FALSE;
477 if(!IS_NULL_PTR(process_dirty_rect)) dt_drawlayer_paint_runtime_state_reset(process_dirty_rect);
478
479 if(!IS_NULL_PTR(process_stroke_mask) && process_stroke_mask->pixels
480 && process_stroke_mask->width == process_patch->width
481 && process_stroke_mask->height == process_patch->height)
482 {
483 const gboolean have_base_mask = base_stroke_mask && base_stroke_mask->pixels
484 && base_stroke_mask->width > 0 && base_stroke_mask->height > 0;
485 if(!have_base_mask)
486 memset(process_stroke_mask->pixels, 0,
487 (size_t)process_stroke_mask->width * process_stroke_mask->height * sizeof(float));
488 else if(fabs(combined_roi->scale - 1.0) <= 1e-6)
489 {
490 const int src_x0 = MAX(0, combined_roi->x);
491 const int src_y0 = MAX(0, combined_roi->y);
492 const int src_x1 = MIN(base_stroke_mask->width, combined_roi->x + combined_roi->width);
493 const int src_y1 = MIN(base_stroke_mask->height, combined_roi->y + combined_roi->height);
494 const int copy_w = src_x1 - src_x0;
495 const int copy_h = src_y1 - src_y0;
496 memset(process_stroke_mask->pixels, 0,
497 (size_t)process_stroke_mask->width * process_stroke_mask->height * sizeof(float));
498 if(copy_w > 0 && copy_h > 0)
499 {
500 const int dst_x0 = src_x0 - combined_roi->x;
501 const int dst_y0 = src_y0 - combined_roi->y;
502 __OMP_PARALLEL_FOR__(if(copy_h > 8))
503 for(int y = 0; y < copy_h; y++)
504 {
505 const float *src = base_stroke_mask->pixels + (size_t)(src_y0 + y) * base_stroke_mask->width + src_x0;
506 float *dst = process_stroke_mask->pixels + (size_t)(dst_y0 + y) * process_stroke_mask->width + dst_x0;
507 memcpy(dst, src, (size_t)copy_w * sizeof(float));
508 }
509 }
510 }
511 else
512 {
513 __OMP_PARALLEL_FOR__(if(process_stroke_mask->height > 8))
514 for(int y = 0; y < process_stroke_mask->height; y++)
515 {
516 const float src_y = ((float)y + 0.5f) / combined_roi->scale + (float)combined_roi->y - 0.5f;
517 const int sy = CLAMP((int)lroundf(src_y), 0, base_stroke_mask->height - 1);
518 float *dst = process_stroke_mask->pixels + (size_t)y * process_stroke_mask->width;
519 for(int x = 0; x < process_stroke_mask->width; x++)
520 {
521 const float src_x = ((float)x + 0.5f) / combined_roi->scale + (float)combined_roi->x - 0.5f;
522 const int sx = CLAMP((int)lroundf(src_x), 0, base_stroke_mask->width - 1);
523 dst[x] = base_stroke_mask->pixels[(size_t)sy * base_stroke_mask->width + sx];
524 }
525 }
526 }
527 }
528#ifdef HAVE_OPENCL
530#endif
531 return TRUE;
532}
533
536 dt_drawlayer_cache_patch_t *base_stroke_mask,
537 const dt_iop_roi_t *process_combined_roi,
538 dt_drawlayer_cache_patch_t *process_patch,
539 dt_drawlayer_cache_patch_t *process_stroke_mask,
540 float **process_update_pixels,
541 size_t *process_update_capacity_pixels,
542 gboolean *cache_dirty, gboolean *process_patch_dirty,
543 dt_drawlayer_damaged_rect_t *process_dirty_rect,
544 const char *update_buffer_name)
545{
546 if(IS_NULL_PTR(base_patch) || IS_NULL_PTR(process_combined_roi) || IS_NULL_PTR(process_patch)
547 || IS_NULL_PTR(process_update_pixels) || IS_NULL_PTR(process_update_capacity_pixels))
548 return FALSE;
549 if(IS_NULL_PTR(process_patch->pixels) || IS_NULL_PTR(base_patch->pixels) || IS_NULL_PTR(process_patch_dirty) || !*process_patch_dirty) return TRUE;
550 if(process_patch->width <= 0 || process_patch->height <= 0 || base_patch->width <= 0 || base_patch->height <= 0)
551 return TRUE;
552 if(process_combined_roi->scale <= 1e-6f) return TRUE;
553
554 const float inv_scale = 1.0f / process_combined_roi->scale;
555 int src_x0 = MAX((int)floorf(process_combined_roi->x * inv_scale), 0);
556 int src_y0 = MAX((int)floorf(process_combined_roi->y * inv_scale), 0);
557 int src_x1 = MIN((int)ceilf((process_combined_roi->x + process_patch->width) * inv_scale), base_patch->width);
558 int src_y1 = MIN((int)ceilf((process_combined_roi->y + process_patch->height) * inv_scale), base_patch->height);
559
560 const gboolean has_dirty_bounds = process_dirty_rect && process_dirty_rect->valid;
561 if(has_dirty_bounds)
562 {
563 const int dirty_x0 = CLAMP(process_dirty_rect->nw[0], 0, process_patch->width);
564 const int dirty_y0 = CLAMP(process_dirty_rect->nw[1], 0, process_patch->height);
565 const int dirty_x1 = CLAMP(process_dirty_rect->se[0], 0, process_patch->width);
566 const int dirty_y1 = CLAMP(process_dirty_rect->se[1], 0, process_patch->height);
567 src_x0 = MAX((int)floorf((dirty_x0 + process_combined_roi->x) * inv_scale), 0);
568 src_y0 = MAX((int)floorf((dirty_y0 + process_combined_roi->y) * inv_scale), 0);
569 src_x1 = MIN((int)ceilf((dirty_x1 + process_combined_roi->x) * inv_scale), base_patch->width);
570 src_y1 = MIN((int)ceilf((dirty_y1 + process_combined_roi->y) * inv_scale), base_patch->height);
571 }
572
573 if(src_x1 <= src_x0 || src_y1 <= src_y0) return TRUE;
574
575 const int dst_w = src_x1 - src_x0;
576 const int dst_h = src_y1 - src_y0;
577 const size_t needed_pixels = (size_t)dst_w * dst_h;
578 float *update_buffer = dt_drawlayer_cache_ensure_scratch_buffer(process_update_pixels,
579 process_update_capacity_pixels,
580 needed_pixels, update_buffer_name);
581 if(!update_buffer)
582 return FALSE;
583
584 const dt_iop_roi_t process_roi = {
585 .x = 0,
586 .y = 0,
587 .width = process_patch->width,
588 .height = process_patch->height,
589 .scale = 1.0f,
590 };
591 const dt_iop_roi_t inverse_roi = {
592 .x = (int)lroundf((float)src_x0 - process_combined_roi->x * inv_scale),
593 .y = (int)lroundf((float)src_y0 - process_combined_roi->y * inv_scale),
594 .width = dst_w,
595 .height = dst_h,
596 .scale = inv_scale,
597 };
598
599 dt_iop_clip_and_zoom(update_buffer, process_patch->pixels, &inverse_roi, &process_roi, dst_w, process_patch->width);
600
602 __OMP_PARALLEL_FOR__(if(dst_h > 8))
603 for(int yy = 0; yy < dst_h; yy++)
604 {
605 memcpy(base_patch->pixels + 4 * ((size_t)(src_y0 + yy) * base_patch->width + src_x0),
606 update_buffer + 4 * ((size_t)yy * dst_w),
607 (size_t)dst_w * 4 * sizeof(float));
608 }
609#ifdef HAVE_OPENCL
611#endif
613
614 if(!IS_NULL_PTR(base_stroke_mask) && !IS_NULL_PTR(process_stroke_mask) && base_stroke_mask->pixels && process_stroke_mask->pixels
615 && base_stroke_mask->width > 0 && base_stroke_mask->height > 0
616 && process_stroke_mask->width == process_patch->width
617 && process_stroke_mask->height == process_patch->height)
618 {
619 float *mask_update_buffer = dt_drawlayer_cache_ensure_scratch_buffer(process_update_pixels,
620 process_update_capacity_pixels,
621 needed_pixels,
622 update_buffer_name);
623 if(IS_NULL_PTR(mask_update_buffer)) return FALSE;
624
625 dt_iop_clip_and_zoom(mask_update_buffer, process_stroke_mask->pixels, &inverse_roi, &process_roi,
626 dst_w, process_stroke_mask->width);
627 __OMP_PARALLEL_FOR__(if(dst_h > 8))
628 for(int yy = 0; yy < dst_h; yy++)
629 {
630 memcpy(base_stroke_mask->pixels + (size_t)(src_y0 + yy) * base_stroke_mask->width + src_x0,
631 mask_update_buffer + (size_t)yy * dst_w,
632 (size_t)dst_w * sizeof(float));
633 }
634 }
635
636 if(cache_dirty) *cache_dirty = TRUE;
637 *process_patch_dirty = FALSE;
638 if(!IS_NULL_PTR(process_dirty_rect)) dt_drawlayer_paint_runtime_state_reset(process_dirty_rect);
639 return TRUE;
640}
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
char * name
darktable_t darktable
Definition darktable.c:181
void dt_pixelpipe_cache_free_align_cache(struct dt_dev_pixelpipe_cache_t *cache, void **mem, const char *message)
#define dt_free(ptr)
Definition darktable.h:456
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:258
void * dt_pixelpipe_cache_alloc_align_cache_impl(struct dt_dev_pixelpipe_cache_t *cache, size_t size, int id, const char *name)
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:281
void dt_iop_clip_and_zoom(float *out, const float *const in, const dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const int32_t out_stride, const int32_t in_stride)
Patch/cache helpers for drawlayer process and preview buffers.
void dt_drawlayer_paint_runtime_state_reset(dt_drawlayer_damaged_rect_t *state)
Reset stroke-damage accumulator to empty/invalid.
static const float x
@ DT_DEV_PIXELPIPE_NONE
Definition pixelpipe.h:37
void dt_dev_pixelpipe_cache_ref_count_entry(dt_dev_pixelpipe_cache_t *cache, gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Increase/Decrease the reference count on the cache line as to prevent LRU item removal....
int dt_dev_pixelpipe_cache_get(dt_dev_pixelpipe_cache_t *cache, const uint64_t hash, const size_t size, const char *name, const int id, const gboolean alloc, void **data, dt_pixel_cache_entry_t **entry)
Get a cache line from the cache.
void dt_dev_pixelpipe_cache_wrlock_entry(dt_dev_pixelpipe_cache_t *cache, gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Lock or release the write lock on the entry.
gboolean dt_dev_pixelpipe_cache_flush_host_pinned_image(dt_dev_pixelpipe_cache_t *cache, void *host_ptr, dt_pixel_cache_entry_t *entry_hint, int devid)
Drop cached pinned OpenCL images associated with a given host buffer.
void dt_dev_pixelpipe_cache_rdlock_entry(dt_dev_pixelpipe_cache_t *cache, gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Lock or release the read lock on the entry.
dt_pixel_cache_entry_t * dt_dev_pixelpipe_cache_ref_entry_for_host_ptr(dt_dev_pixelpipe_cache_t *cache, void *host_ptr)
Resolve and retain the cache entry owning a host pointer.
#define DT_PIXELPIPE_CACHE_HASH_INVALID
void * dt_drawlayer_cache_alloc_temp_buffer(const size_t bytes, const char *name)
Allocate temporary aligned cache buffer.
void dt_drawlayer_cache_invalidate_process_patch_state(gboolean *process_patch_valid, gboolean *process_patch_dirty, dt_drawlayer_damaged_rect_t *process_dirty_rect, int *process_patch_padding, dt_iop_roi_t *process_combined_roi)
Reset process-patch validity and dirty-state bookkeeping.
float * dt_drawlayer_cache_ensure_scratch_buffer(float **buffer, size_t *capacity_pixels, const size_t needed_pixels, const char *name)
Ensure scratch RGBA float capacity in pixels.
void dt_drawlayer_cache_patch_wrunlock(const dt_drawlayer_cache_patch_t *patch)
Release write lock on shared patch cache entry.
gboolean dt_drawlayer_cache_flush_process_patch_to_base(dt_drawlayer_cache_patch_t *base_patch, dt_drawlayer_cache_patch_t *base_stroke_mask, const dt_iop_roi_t *process_combined_roi, dt_drawlayer_cache_patch_t *process_patch, dt_drawlayer_cache_patch_t *process_stroke_mask, float **process_update_pixels, size_t *process_update_capacity_pixels, gboolean *cache_dirty, gboolean *process_patch_dirty, dt_drawlayer_damaged_rect_t *process_dirty_rect, const char *update_buffer_name)
Flush dirty process patch region back into the base patch.
void dt_drawlayer_cache_patch_wrlock(const dt_drawlayer_cache_patch_t *patch)
Acquire write lock on shared patch cache entry.
void dt_drawlayer_cache_patch_clear(dt_drawlayer_cache_patch_t *patch, const char *external_alloc_name)
Release patch storage and reset patch metadata.
gboolean dt_drawlayer_cache_ensure_mask_buffer(dt_drawlayer_cache_patch_t *mask, const int width, const int height, const char *name)
Ensure a float stroke-mask buffer exists and matches the requested size.
gboolean dt_drawlayer_cache_patch_alloc_shared(dt_drawlayer_cache_patch_t *patch, const uint64_t hash, const size_t pixel_count, const int width, const int height, const char *name, int *created_out)
Allocate patch storage from shared pixel cache entry.
void dt_drawlayer_cache_build_combined_process_roi_for_piece(const dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t *process_roi, const int current_full_w, const int current_full_h, const int src_w, const int src_h, dt_iop_roi_t *combined_roi)
Build combined ROI using module origin heuristics from piece buffers.
void dt_drawlayer_cache_build_combined_process_roi(const dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t *process_roi, const int current_full_w, const int current_full_h, const int src_w, const int src_h, const int module_origin_x, const int module_origin_y, dt_iop_roi_t *combined_roi)
Build combined ROI mapping process tile coordinates to fitted source image.
gboolean dt_drawlayer_cache_resample_process_patch_to_output(const dt_drawlayer_cache_patch_t *process_patch, const int process_patch_padding, const dt_iop_roi_t *roi_out, float *layerbuf, const int layerbuf_width)
Copy or resample process patch into output layer buffer.
gboolean dt_drawlayer_cache_populate_process_patch_from_base(const dt_drawlayer_cache_patch_t *base_patch, const dt_drawlayer_cache_patch_t *base_stroke_mask, dt_drawlayer_cache_patch_t *process_patch, dt_drawlayer_cache_patch_t *process_stroke_mask, const dt_iop_roi_t *combined_roi, const int process_pad, const int patch_width, const int patch_height, gboolean *process_patch_valid, gboolean *process_patch_dirty, dt_drawlayer_damaged_rect_t *process_dirty_rect, int *process_patch_padding, dt_iop_roi_t *process_combined_roi, const char *patch_buffer_name, const char *mask_buffer_name)
Populate process patch from base patch using ROI crop/scale rules.
gboolean dt_drawlayer_cache_build_process_blend_rois(const dt_drawlayer_cache_patch_t *process_patch, const int process_patch_padding, const dt_iop_roi_t *roi_out, dt_iop_roi_t *blend_target_roi, dt_iop_roi_t *source_process_roi, gboolean *direct_copy)
Resolve source/target ROIs used when blending process patch to output ROI.
gboolean dt_drawlayer_cache_ensure_process_patch_buffer(dt_drawlayer_cache_patch_t *process_patch, dt_drawlayer_cache_patch_t *process_stroke_mask, const int width, const int height, const char *patch_buffer_name, const char *mask_buffer_name)
Ensure process patch and process-stroke-mask backing buffers exist.
void dt_drawlayer_cache_free_temp_buffer(void **buffer, const char *name)
Free temporary aligned cache buffer.
void dt_drawlayer_cache_patch_rdlock(const dt_drawlayer_cache_patch_t *patch)
Acquire read lock on shared patch cache entry.
void dt_drawlayer_cache_resolve_piece_input_origin(const dt_dev_pixelpipe_iop_t *piece, const int current_full_w, const int current_full_h, int *module_origin_x, int *module_origin_y)
Build combined ROI using module origin heuristics from piece buffers.
void dt_drawlayer_cache_patch_rdunlock(const dt_drawlayer_cache_patch_t *patch)
Release read lock on shared patch cache entry.
void dt_drawlayer_cache_clear_transparent_float(float *pixels, const size_t pixel_count)
Fill float RGBA buffer with transparent black.
unsigned __int64 uint64_t
Definition strptime.c:75
struct dt_dev_pixelpipe_cache_t * pixelpipe_cache
Definition darktable.h:790
Generic float RGBA patch stored either in malloc memory or pixel cache.
dt_pixel_cache_entry_t * cache_entry
Integer axis-aligned rectangle in buffer coordinates.
Region of interest passed through the pixelpipe.
Definition imageop.h:72
double scale
Definition imageop.h:74
uint64_t hash
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29