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 "system/macros.h"
20#include "system/openmp.h"
21#include "system/mem_alloc.h"
22#include "iop/drawlayer/cache.h"
23
25
26#include <math.h>
27#include <string.h>
28
30void *dt_drawlayer_cache_alloc_temp_buffer(const size_t bytes, const char *name)
31{
32 if(bytes == 0) return NULL;
34}
35
37void dt_drawlayer_cache_free_temp_buffer(void **buffer, const char *name)
38{
39 if(IS_NULL_PTR(buffer) || !*buffer) return;
41}
42
44float *dt_drawlayer_cache_ensure_scratch_buffer(float **buffer, size_t *capacity_pixels, const size_t needed_pixels,
45 const char *name)
46{
47 if(IS_NULL_PTR(buffer) || IS_NULL_PTR(capacity_pixels) || needed_pixels == 0) return NULL;
48 if(*capacity_pixels < needed_pixels)
49 {
51 float *new_buffer = dt_drawlayer_cache_alloc_temp_buffer(needed_pixels * 4 * sizeof(float), name);
52 if(IS_NULL_PTR(new_buffer)) return NULL;
53 *buffer = new_buffer;
54 *capacity_pixels = needed_pixels;
55 }
56 return *buffer;
57}
58
60void dt_drawlayer_cache_clear_transparent_float(float *pixels, const size_t pixel_count)
61{
62 if(IS_NULL_PTR(pixels)) return;
63 memset(pixels, 0, pixel_count * 4 * sizeof(float));
64}
65
67void dt_drawlayer_cache_patch_clear(dt_drawlayer_cache_patch_t *patch, const char *external_alloc_name)
68{
69 if(IS_NULL_PTR(patch)) return;
70 if(patch->external_alloc)
71 {
72 if(patch->cache_entry)
74 void *buffer = patch->pixels;
75 dt_pixelpipe_cache_free_align_cache(&buffer, external_alloc_name);
76 }
77 else if(patch->cache_entry)
78 {
80 }
81 else
82 {
83 dt_free(patch->pixels);
84 }
85 memset(patch, 0, sizeof(*patch));
86}
87
90 const size_t pixel_count, const int width, const int height,
91 const char *name, int *created_out)
92{
93 if(IS_NULL_PTR(patch) || pixel_count == 0 || width <= 0 || height <= 0) return FALSE;
94
95 void *data = NULL;
96 dt_pixel_cache_entry_t *entry = NULL;
97 const int created = dt_dev_pixelpipe_cache_get(hash, pixel_count * 4 * sizeof(float),
98 name, DT_DEV_PIXELPIPE_NONE, TRUE, &data, &entry);
99 if(!IS_NULL_PTR(created_out)) *created_out = created;
100 if(IS_NULL_PTR(data) || IS_NULL_PTR(entry))
101 {
102 if(entry)
103 {
104 if(created) dt_dev_pixelpipe_cache_wrlock_entry(FALSE, entry);
106 }
107 return FALSE;
108 }
109
110 dt_drawlayer_cache_patch_clear(patch, "drawlayer patch");
111 patch->x = 0;
112 patch->y = 0;
113 patch->width = width;
114 patch->height = height;
115 patch->pixels = (float *)data;
116 patch->cache_entry = entry;
117 patch->cache_hash = hash;
118 patch->external_alloc = FALSE;
119
120 if(created) dt_dev_pixelpipe_cache_wrlock_entry(FALSE, entry);
121 return TRUE;
122}
123
126 const int height, const char *name)
127{
128 if(IS_NULL_PTR(mask) || width <= 0 || height <= 0) return FALSE;
129
130 const gboolean size_changed = (mask->width != width || mask->height != height || !mask->pixels);
131 if(size_changed)
132 {
134 mask->width = width;
135 mask->height = height;
136 mask->x = 0;
137 mask->y = 0;
138 mask->pixels = dt_drawlayer_cache_alloc_temp_buffer((size_t)width * height * sizeof(float), name);
139 mask->external_alloc = TRUE;
140 if(IS_NULL_PTR(mask->pixels))
141 {
142 mask->width = 0;
143 mask->height = 0;
144 return FALSE;
145 }
146
147 mask->cache_entry = dt_dev_pixelpipe_cache_ref_entry_for_host_ptr(mask->pixels);
148 mask->cache_hash = mask->cache_entry ? mask->cache_entry->hash : DT_PIXELPIPE_CACHE_HASH_INVALID;
149 if(IS_NULL_PTR(mask->cache_entry))
150 {
152 return FALSE;
153 }
154 }
155
156 memset(mask->pixels, 0, (size_t)width * height * sizeof(float));
157 return TRUE;
158}
159
166
173
180
187
189void dt_drawlayer_cache_invalidate_process_patch_state(gboolean *process_patch_valid, gboolean *process_patch_dirty,
190 dt_drawlayer_damaged_rect_t *process_dirty_rect,
191 int *process_patch_padding,
192 dt_iop_roi_t *process_combined_roi)
193{
194 if(process_patch_valid) *process_patch_valid = FALSE;
195 if(process_patch_dirty) *process_patch_dirty = FALSE;
196 if(!IS_NULL_PTR(process_dirty_rect)) dt_drawlayer_paint_runtime_state_reset(process_dirty_rect);
197 if(!IS_NULL_PTR(process_patch_padding)) *process_patch_padding = 0;
198 if(!IS_NULL_PTR(process_combined_roi)) memset(process_combined_roi, 0, sizeof(*process_combined_roi));
199}
200
203 dt_drawlayer_cache_patch_t *process_stroke_mask,
204 const int width,
205 const int height, const char *patch_buffer_name,
206 const char *mask_buffer_name)
207{
208 if(IS_NULL_PTR(process_patch) || !process_stroke_mask || width <= 0 || height <= 0)
209 return FALSE;
210
211 const gboolean size_changed = (process_patch->width != width || process_patch->height != height
212 || !process_patch->pixels);
213 if(size_changed)
214 {
215 dt_drawlayer_cache_patch_clear(process_patch, patch_buffer_name);
216 process_patch->width = width;
217 process_patch->height = height;
218 process_patch->x = 0;
219 process_patch->y = 0;
220 process_patch->pixels = dt_drawlayer_cache_alloc_temp_buffer((gsize)width * height * 4 * sizeof(float),
221 patch_buffer_name);
222 process_patch->external_alloc = TRUE;
223 if(IS_NULL_PTR(process_patch->pixels)) return FALSE;
224 process_patch->cache_entry
226 process_patch->cache_hash = process_patch->cache_entry ? process_patch->cache_entry->hash
228 if(IS_NULL_PTR(process_patch->cache_entry)) return FALSE;
229 }
230
231 const size_t mask_count = (size_t)width * height;
232 if(size_changed || process_stroke_mask->width != width || process_stroke_mask->height != height
233 || !process_stroke_mask->pixels)
234 {
235 dt_drawlayer_cache_patch_clear(process_stroke_mask, mask_buffer_name);
236 process_stroke_mask->width = width;
237 process_stroke_mask->height = height;
238 process_stroke_mask->x = 0;
239 process_stroke_mask->y = 0;
240 process_stroke_mask->pixels = dt_drawlayer_cache_alloc_temp_buffer(mask_count * sizeof(float),
241 mask_buffer_name);
242 process_stroke_mask->external_alloc = TRUE;
243 if(!process_stroke_mask->pixels)
244 {
245 process_stroke_mask->width = 0;
246 process_stroke_mask->height = 0;
247 return FALSE;
248 }
249 process_stroke_mask->cache_entry
251 process_stroke_mask->cache_hash = process_stroke_mask->cache_entry ? process_stroke_mask->cache_entry->hash
253 if(!process_stroke_mask->cache_entry)
254 {
255 process_stroke_mask->width = 0;
256 process_stroke_mask->height = 0;
257 return FALSE;
258 }
259 }
260
261 return TRUE;
262}
263
266 const dt_iop_roi_t *process_roi, const int current_full_w,
267 const int current_full_h, const int src_w, const int src_h,
268 const int module_origin_x, const int module_origin_y,
269 dt_iop_roi_t *combined_roi)
270{
271 if(IS_NULL_PTR(piece) || IS_NULL_PTR(process_roi) || IS_NULL_PTR(combined_roi) || current_full_w <= 0 || current_full_h <= 0
272 || src_w <= 0 || src_h <= 0)
273 {
274 if(!IS_NULL_PTR(combined_roi)) memset(combined_roi, 0, sizeof(*combined_roi));
275 return;
276 }
277
278 const float fit = fminf((float)current_full_w / (float)src_w, (float)current_full_h / (float)src_h);
279 const int scaled_w = MAX(1, MIN(current_full_w, (int)lroundf(src_w * fit)));
280 const int scaled_h = MAX(1, MIN(current_full_h, (int)lroundf(src_h * fit)));
281 const int fit_offset_x = MAX((current_full_w - scaled_w) / 2, 0);
282 const int fit_offset_y = MAX((current_full_h - scaled_h) / 2, 0);
283
284 const float inv_scale = process_roi->scale > 1e-6f ? (1.0f / process_roi->scale) : 0.0f;
285 const float tile_origin_canvas_x = (float)module_origin_x + process_roi->x * inv_scale;
286 const float tile_origin_canvas_y = (float)module_origin_y + process_roi->y * inv_scale;
287
288 combined_roi->x = (int)lroundf((tile_origin_canvas_x - fit_offset_x) * process_roi->scale);
289 combined_roi->y = (int)lroundf((tile_origin_canvas_y - fit_offset_y) * process_roi->scale);
290 combined_roi->width = process_roi->width;
291 combined_roi->height = process_roi->height;
292 combined_roi->scale = process_roi->scale * fmaxf(fit, 1e-6f);
293}
294
297 const int current_full_w, const int current_full_h,
298 int *module_origin_x, int *module_origin_y)
299{
300 int origin_x = 0;
301 int origin_y = 0;
302
303 if(!IS_NULL_PTR(piece))
304 {
305 origin_x = piece->buf_in.x;
306 origin_y = piece->buf_in.y;
307
308 if(origin_x == 0 && piece->buf_in.width < current_full_w && piece->buf_in.height == current_full_h)
309 origin_x = MAX((current_full_w - piece->buf_in.width) / 2, 0);
310 if(origin_y == 0 && piece->buf_in.height < current_full_h && piece->buf_in.width == current_full_w)
311 origin_y = MAX((current_full_h - piece->buf_in.height) / 2, 0);
312 }
313
314 if(module_origin_x) *module_origin_x = origin_x;
315 if(module_origin_y) *module_origin_y = origin_y;
316}
317
320 const dt_iop_roi_t *process_roi,
321 const int current_full_w,
322 const int current_full_h,
323 const int src_w, const int src_h,
324 dt_iop_roi_t *combined_roi)
325{
326 if(IS_NULL_PTR(piece) || IS_NULL_PTR(process_roi) || IS_NULL_PTR(combined_roi) || current_full_w <= 0 || current_full_h <= 0
327 || src_w <= 0 || src_h <= 0)
328 {
329 if(!IS_NULL_PTR(combined_roi)) memset(combined_roi, 0, sizeof(*combined_roi));
330 return;
331 }
332
333 int module_origin_x = 0;
334 int module_origin_y = 0;
335 dt_drawlayer_cache_resolve_piece_input_origin(piece, current_full_w, current_full_h,
336 &module_origin_x, &module_origin_y);
337
338 dt_drawlayer_cache_build_combined_process_roi(piece, process_roi, current_full_w, current_full_h,
339 src_w, src_h, module_origin_x, module_origin_y, combined_roi);
340}
341
344 const int process_patch_padding,
345 const dt_iop_roi_t *roi_out,
346 dt_iop_roi_t *blend_target_roi,
347 dt_iop_roi_t *source_process_roi,
348 gboolean *direct_copy)
349{
350 if(IS_NULL_PTR(process_patch) || IS_NULL_PTR(roi_out) || IS_NULL_PTR(direct_copy)) return FALSE;
351 if(process_patch->width <= 0 || process_patch->height <= 0 || roi_out->width <= 0 || roi_out->height <= 0)
352 return FALSE;
353
354 if(!IS_NULL_PTR(source_process_roi))
355 {
356 source_process_roi->x = 0;
357 source_process_roi->y = 0;
358 source_process_roi->width = process_patch->width;
359 source_process_roi->height = process_patch->height;
360 source_process_roi->scale = 1.0f;
361 }
362
363 const int process_pad = MAX(process_patch_padding, 0);
364 if(!IS_NULL_PTR(blend_target_roi))
365 {
366 blend_target_roi->x = process_pad;
367 blend_target_roi->y = process_pad;
368 blend_target_roi->width = roi_out->width;
369 blend_target_roi->height = roi_out->height;
370 blend_target_roi->scale = 1.0f;
371 }
372
373 *direct_copy = (process_patch->width == roi_out->width && process_patch->height == roi_out->height);
374 return TRUE;
375}
376
379 const int process_patch_padding,
380 const dt_iop_roi_t *roi_out,
381 float *layerbuf,
382 const int layerbuf_width)
383{
384 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;
385
386 dt_iop_roi_t target_roi = { 0 };
387 dt_iop_roi_t source_process_roi = { 0 };
388 gboolean direct_copy = FALSE;
389 if(!dt_drawlayer_cache_build_process_blend_rois(process_patch, process_patch_padding, roi_out,
390 &target_roi, &source_process_roi, &direct_copy))
391 return FALSE;
392
393 if(direct_copy)
394 {
395 memcpy(layerbuf, process_patch->pixels, (size_t)roi_out->width * roi_out->height * 4 * sizeof(float));
396 return TRUE;
397 }
398
399 dt_iop_clip_and_zoom(layerbuf, process_patch->pixels, &target_roi, &source_process_roi,
400 layerbuf_width, process_patch->width);
401 return TRUE;
402}
403
406 const dt_drawlayer_cache_patch_t *base_stroke_mask,
407 dt_drawlayer_cache_patch_t *process_patch,
408 dt_drawlayer_cache_patch_t *process_stroke_mask,
409 const dt_iop_roi_t *combined_roi, const int process_pad,
410 const int patch_width, const int patch_height,
411 gboolean *process_patch_valid,
412 gboolean *process_patch_dirty,
413 dt_drawlayer_damaged_rect_t *process_dirty_rect,
414 int *process_patch_padding,
415 dt_iop_roi_t *process_combined_roi,
416 const char *patch_buffer_name,
417 const char *mask_buffer_name)
418{
419 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
420 || base_patch->height <= 0 || patch_width <= 0 || patch_height <= 0 || combined_roi->scale <= 1e-6f)
421 return FALSE;
422
423 process_patch->x = 0;
424 process_patch->y = 0;
425 if(!IS_NULL_PTR(process_patch_padding)) *process_patch_padding = process_pad;
426 if(!IS_NULL_PTR(process_combined_roi)) *process_combined_roi = *combined_roi;
427
429 if(fabs(combined_roi->scale - 1.0) <= 1e-6f)
430 {
431 const int src_x0 = MAX(0, combined_roi->x);
432 const int src_y0 = MAX(0, combined_roi->y);
433 const int src_x1 = MIN(base_patch->width, combined_roi->x + combined_roi->width);
434 const int src_y1 = MIN(base_patch->height, combined_roi->y + combined_roi->height);
435 const int copy_w = src_x1 - src_x0;
436 const int copy_h = src_y1 - src_y0;
437
438 if(copy_w > 0 && copy_h > 0)
439 {
440 const int dst_x0 = src_x0 - combined_roi->x;
441 const int dst_y0 = src_y0 - combined_roi->y;
442 const gboolean full_coverage = (dst_x0 == 0 && dst_y0 == 0
443 && copy_w == process_patch->width
444 && copy_h == process_patch->height);
445 if(!full_coverage)
446 {
447 memset(process_patch->pixels, 0,
448 (size_t)process_patch->width * process_patch->height * 4 * sizeof(float));
449 }
450 __OMP_PARALLEL_FOR__(if(copy_h > 8))
451 for(int y = 0; y < copy_h; y++)
452 {
453 const float *src = base_patch->pixels + 4 * ((size_t)(src_y0 + y) * base_patch->width + src_x0);
454 float *dst = process_patch->pixels + 4 * ((size_t)(dst_y0 + y) * process_patch->width + dst_x0);
455 memcpy(dst, src, (size_t)copy_w * 4 * sizeof(float));
456 }
457 }
458 else
459 {
460 memset(process_patch->pixels, 0, (size_t)process_patch->width * process_patch->height * 4 * sizeof(float));
461 }
462 }
463 else
464 {
465 const dt_iop_roi_t source_full_roi = {
466 .x = 0,
467 .y = 0,
468 .width = base_patch->width,
469 .height = base_patch->height,
470 .scale = 1.0f,
471 };
472 dt_iop_clip_and_zoom(process_patch->pixels, base_patch->pixels, combined_roi, &source_full_roi,
473 process_patch->width, base_patch->width);
474 }
476
477 if(process_patch_valid) *process_patch_valid = TRUE;
478 if(process_patch_dirty) *process_patch_dirty = FALSE;
479 if(!IS_NULL_PTR(process_dirty_rect)) dt_drawlayer_paint_runtime_state_reset(process_dirty_rect);
480
481 if(!IS_NULL_PTR(process_stroke_mask) && process_stroke_mask->pixels
482 && process_stroke_mask->width == process_patch->width
483 && process_stroke_mask->height == process_patch->height)
484 {
485 const gboolean have_base_mask = base_stroke_mask && base_stroke_mask->pixels
486 && base_stroke_mask->width > 0 && base_stroke_mask->height > 0;
487 if(!have_base_mask)
488 memset(process_stroke_mask->pixels, 0,
489 (size_t)process_stroke_mask->width * process_stroke_mask->height * sizeof(float));
490 else if(fabs(combined_roi->scale - 1.0) <= 1e-6)
491 {
492 const int src_x0 = MAX(0, combined_roi->x);
493 const int src_y0 = MAX(0, combined_roi->y);
494 const int src_x1 = MIN(base_stroke_mask->width, combined_roi->x + combined_roi->width);
495 const int src_y1 = MIN(base_stroke_mask->height, combined_roi->y + combined_roi->height);
496 const int copy_w = src_x1 - src_x0;
497 const int copy_h = src_y1 - src_y0;
498 memset(process_stroke_mask->pixels, 0,
499 (size_t)process_stroke_mask->width * process_stroke_mask->height * sizeof(float));
500 if(copy_w > 0 && copy_h > 0)
501 {
502 const int dst_x0 = src_x0 - combined_roi->x;
503 const int dst_y0 = src_y0 - combined_roi->y;
504 __OMP_PARALLEL_FOR__(if(copy_h > 8))
505 for(int y = 0; y < copy_h; y++)
506 {
507 const float *src = base_stroke_mask->pixels + (size_t)(src_y0 + y) * base_stroke_mask->width + src_x0;
508 float *dst = process_stroke_mask->pixels + (size_t)(dst_y0 + y) * process_stroke_mask->width + dst_x0;
509 memcpy(dst, src, (size_t)copy_w * sizeof(float));
510 }
511 }
512 }
513 else
514 {
515 __OMP_PARALLEL_FOR__(if(process_stroke_mask->height > 8))
516 for(int y = 0; y < process_stroke_mask->height; y++)
517 {
518 const float src_y = ((float)y + 0.5f) / combined_roi->scale + (float)combined_roi->y - 0.5f;
519 const int sy = CLAMP((int)lroundf(src_y), 0, base_stroke_mask->height - 1);
520 float *dst = process_stroke_mask->pixels + (size_t)y * process_stroke_mask->width;
521 for(int x = 0; x < process_stroke_mask->width; x++)
522 {
523 const float src_x = ((float)x + 0.5f) / combined_roi->scale + (float)combined_roi->x - 0.5f;
524 const int sx = CLAMP((int)lroundf(src_x), 0, base_stroke_mask->width - 1);
525 dst[x] = base_stroke_mask->pixels[(size_t)sy * base_stroke_mask->width + sx];
526 }
527 }
528 }
529 }
530#ifdef HAVE_OPENCL
532#endif
533 return TRUE;
534}
535
538 dt_drawlayer_cache_patch_t *base_stroke_mask,
539 const dt_iop_roi_t *process_combined_roi,
540 dt_drawlayer_cache_patch_t *process_patch,
541 dt_drawlayer_cache_patch_t *process_stroke_mask,
542 float **process_update_pixels,
543 size_t *process_update_capacity_pixels,
544 gboolean *cache_dirty, gboolean *process_patch_dirty,
545 dt_drawlayer_damaged_rect_t *process_dirty_rect,
546 const char *update_buffer_name)
547{
548 if(IS_NULL_PTR(base_patch) || IS_NULL_PTR(process_combined_roi) || IS_NULL_PTR(process_patch)
549 || IS_NULL_PTR(process_update_pixels) || IS_NULL_PTR(process_update_capacity_pixels))
550 return FALSE;
551 if(IS_NULL_PTR(process_patch->pixels) || IS_NULL_PTR(base_patch->pixels) || IS_NULL_PTR(process_patch_dirty) || !*process_patch_dirty) return TRUE;
552 if(process_patch->width <= 0 || process_patch->height <= 0 || base_patch->width <= 0 || base_patch->height <= 0)
553 return TRUE;
554 if(process_combined_roi->scale <= 1e-6f) return TRUE;
555
556 const float inv_scale = 1.0f / process_combined_roi->scale;
557 int src_x0 = MAX((int)floorf(process_combined_roi->x * inv_scale), 0);
558 int src_y0 = MAX((int)floorf(process_combined_roi->y * inv_scale), 0);
559 int src_x1 = MIN((int)ceilf((process_combined_roi->x + process_patch->width) * inv_scale), base_patch->width);
560 int src_y1 = MIN((int)ceilf((process_combined_roi->y + process_patch->height) * inv_scale), base_patch->height);
561
562 const gboolean has_dirty_bounds = process_dirty_rect && process_dirty_rect->valid;
563 if(has_dirty_bounds)
564 {
565 const int dirty_x0 = CLAMP(process_dirty_rect->nw[0], 0, process_patch->width);
566 const int dirty_y0 = CLAMP(process_dirty_rect->nw[1], 0, process_patch->height);
567 const int dirty_x1 = CLAMP(process_dirty_rect->se[0], 0, process_patch->width);
568 const int dirty_y1 = CLAMP(process_dirty_rect->se[1], 0, process_patch->height);
569 src_x0 = MAX((int)floorf((dirty_x0 + process_combined_roi->x) * inv_scale), 0);
570 src_y0 = MAX((int)floorf((dirty_y0 + process_combined_roi->y) * inv_scale), 0);
571 src_x1 = MIN((int)ceilf((dirty_x1 + process_combined_roi->x) * inv_scale), base_patch->width);
572 src_y1 = MIN((int)ceilf((dirty_y1 + process_combined_roi->y) * inv_scale), base_patch->height);
573 }
574
575 if(src_x1 <= src_x0 || src_y1 <= src_y0) return TRUE;
576
577 const int dst_w = src_x1 - src_x0;
578 const int dst_h = src_y1 - src_y0;
579 const size_t needed_pixels = (size_t)dst_w * dst_h;
580 float *update_buffer = dt_drawlayer_cache_ensure_scratch_buffer(process_update_pixels,
581 process_update_capacity_pixels,
582 needed_pixels, update_buffer_name);
583 if(!update_buffer)
584 return FALSE;
585
586 const dt_iop_roi_t process_roi = {
587 .x = 0,
588 .y = 0,
589 .width = process_patch->width,
590 .height = process_patch->height,
591 .scale = 1.0f,
592 };
593 const dt_iop_roi_t inverse_roi = {
594 .x = (int)lroundf((float)src_x0 - process_combined_roi->x * inv_scale),
595 .y = (int)lroundf((float)src_y0 - process_combined_roi->y * inv_scale),
596 .width = dst_w,
597 .height = dst_h,
598 .scale = inv_scale,
599 };
600
601 dt_iop_clip_and_zoom(update_buffer, process_patch->pixels, &inverse_roi, &process_roi, dst_w, process_patch->width);
602
604 __OMP_PARALLEL_FOR__(if(dst_h > 8))
605 for(int yy = 0; yy < dst_h; yy++)
606 {
607 memcpy(base_patch->pixels + 4 * ((size_t)(src_y0 + yy) * base_patch->width + src_x0),
608 update_buffer + 4 * ((size_t)yy * dst_w),
609 (size_t)dst_w * 4 * sizeof(float));
610 }
611#ifdef HAVE_OPENCL
613#endif
615
616 if(!IS_NULL_PTR(base_stroke_mask) && !IS_NULL_PTR(process_stroke_mask) && base_stroke_mask->pixels && process_stroke_mask->pixels
617 && base_stroke_mask->width > 0 && base_stroke_mask->height > 0
618 && process_stroke_mask->width == process_patch->width
619 && process_stroke_mask->height == process_patch->height)
620 {
621 float *mask_update_buffer = dt_drawlayer_cache_ensure_scratch_buffer(process_update_pixels,
622 process_update_capacity_pixels,
623 needed_pixels,
624 update_buffer_name);
625 if(IS_NULL_PTR(mask_update_buffer)) return FALSE;
626
627 dt_iop_clip_and_zoom(mask_update_buffer, process_stroke_mask->pixels, &inverse_roi, &process_roi,
628 dst_w, process_stroke_mask->width);
629 __OMP_PARALLEL_FOR__(if(dst_h > 8))
630 for(int yy = 0; yy < dst_h; yy++)
631 {
632 memcpy(base_stroke_mask->pixels + (size_t)(src_y0 + yy) * base_stroke_mask->width + src_x0,
633 mask_update_buffer + (size_t)yy * dst_w,
634 (size_t)dst_w * sizeof(float));
635 }
636 }
637
638 if(cache_dirty) *cache_dirty = TRUE;
639 *process_patch_dirty = FALSE;
640 if(!IS_NULL_PTR(process_dirty_rect)) dt_drawlayer_paint_runtime_state_reset(process_dirty_rect);
641 return TRUE;
642}
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
static const float x
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.
#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 macros.h:96
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
const char * name
Definition pdf.h:90
@ DT_DEV_PIXELPIPE_NONE
Definition pixelpipe.h:41
void dt_dev_pixelpipe_cache_wrlock_entry(gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Lock or release the write lock on the entry.
void * dt_pixelpipe_cache_alloc_align_cache_impl(size_t size, int id, const char *name)
Allocate aligned memory tracked by the pixelpipe cache. This allows LRU cache entries to be evicted i...
void dt_dev_pixelpipe_cache_ref_count_entry(gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Increase/Decrease the reference count on the cache line as to prevent LRU item removal....
void dt_dev_pixelpipe_cache_rdlock_entry(gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Lock or release the read lock on the entry.
gboolean dt_dev_pixelpipe_cache_flush_host_pinned_image(void *host_ptr, dt_pixel_cache_entry_t *entry_hint, int devid)
Drop cached pinned OpenCL images associated with a given host buffer.
int dt_dev_pixelpipe_cache_get(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_pixelpipe_cache_free_align_cache(void **mem, const char *message)
Free aligned memory allocated with dt_pixelpipe_cache_alloc_align_cache.
dt_pixel_cache_entry_t * dt_dev_pixelpipe_cache_ref_entry_for_host_ptr(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
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 format.h:49
double scale
Definition format.h:51
int width
Definition format.h:50
int height
Definition format.h:50
uint64_t hash
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29