30 if(bytes == 0)
return NULL;
46 if(*capacity_pixels < needed_pixels)
52 *capacity_pixels = needed_pixels;
61 memset(pixels, 0, pixel_count * 4 *
sizeof(
float));
72 void *buffer = patch->
pixels;
83 memset(patch, 0,
sizeof(*patch));
88 const size_t pixel_count,
const int width,
const int height,
89 const char *
name,
int *created_out)
97 if(!
IS_NULL_PTR(created_out)) *created_out = created;
113 patch->
pixels = (
float *)data;
128 const gboolean size_changed = (mask->width !=
width || mask->height !=
height || !mask->pixels);
137 mask->external_alloc =
TRUE;
154 memset(mask->pixels, 0, (
size_t)
width *
height *
sizeof(
float));
189 int *process_patch_padding,
192 if(process_patch_valid) *process_patch_valid =
FALSE;
193 if(process_patch_dirty) *process_patch_dirty =
FALSE;
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));
203 const int height,
const char *patch_buffer_name,
204 const char *mask_buffer_name)
210 || !process_patch->
pixels);
216 process_patch->
x = 0;
217 process_patch->
y = 0;
231 || !process_stroke_mask->
pixels)
236 process_stroke_mask->
x = 0;
237 process_stroke_mask->
y = 0;
241 if(!process_stroke_mask->
pixels)
243 process_stroke_mask->
width = 0;
244 process_stroke_mask->
height = 0;
253 process_stroke_mask->
width = 0;
254 process_stroke_mask->
height = 0;
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,
270 || src_w <= 0 || src_h <= 0)
272 if(!
IS_NULL_PTR(combined_roi)) memset(combined_roi, 0,
sizeof(*combined_roi));
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);
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;
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);
290 combined_roi->
scale = process_roi->
scale * fmaxf(fit, 1e-6f);
295 const int current_full_w,
const int current_full_h,
296 int *module_origin_x,
int *module_origin_y)
312 if(module_origin_x) *module_origin_x = origin_x;
313 if(module_origin_y) *module_origin_y = origin_y;
319 const int current_full_w,
320 const int current_full_h,
321 const int src_w,
const int src_h,
325 || src_w <= 0 || src_h <= 0)
327 if(!
IS_NULL_PTR(combined_roi)) memset(combined_roi, 0,
sizeof(*combined_roi));
331 int module_origin_x = 0;
332 int module_origin_y = 0;
334 &module_origin_x, &module_origin_y);
337 src_w, src_h, module_origin_x, module_origin_y, combined_roi);
342 const int process_patch_padding,
346 gboolean *direct_copy)
349 if(process_patch->
width <= 0 || process_patch->
height <= 0 || roi_out->
width <= 0 || roi_out->
height <= 0)
354 source_process_roi->
x = 0;
355 source_process_roi->
y = 0;
356 source_process_roi->
width = process_patch->
width;
358 source_process_roi->
scale = 1.0f;
361 const int process_pad =
MAX(process_patch_padding, 0);
364 blend_target_roi->
x = process_pad;
365 blend_target_roi->
y = process_pad;
368 blend_target_roi->
scale = 1.0f;
377 const int process_patch_padding,
380 const int layerbuf_width)
386 gboolean direct_copy =
FALSE;
388 &target_roi, &source_process_roi, &direct_copy))
393 memcpy(layerbuf, process_patch->
pixels, (
size_t)roi_out->
width * roi_out->
height * 4 *
sizeof(
float));
398 layerbuf_width, process_patch->
width);
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,
412 int *process_patch_padding,
414 const char *patch_buffer_name,
415 const char *mask_buffer_name)
418 || base_patch->
height <= 0 || patch_width <= 0 || patch_height <= 0 || combined_roi->scale <= 1e-6f)
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;
427 if(fabs(combined_roi->
scale - 1.0) <= 1e-6f)
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;
436 if(copy_w > 0 && copy_h > 0)
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);
445 memset(process_patch->
pixels, 0,
446 (
size_t)process_patch->
width * process_patch->
height * 4 *
sizeof(
float));
449 for(
int y = 0; y < copy_h; y++)
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));
458 memset(process_patch->
pixels, 0, (
size_t)process_patch->
width * process_patch->
height * 4 *
sizeof(
float));
466 .width = base_patch->
width,
467 .height = base_patch->
height,
475 if(process_patch_valid) *process_patch_valid =
TRUE;
476 if(process_patch_dirty) *process_patch_dirty =
FALSE;
480 && process_stroke_mask->
width == process_patch->
width
481 && process_stroke_mask->
height == process_patch->
height)
483 const gboolean have_base_mask = base_stroke_mask && base_stroke_mask->
pixels
484 && base_stroke_mask->
width > 0 && base_stroke_mask->
height > 0;
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)
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)
500 const int dst_x0 = src_x0 - combined_roi->
x;
501 const int dst_y0 = src_y0 - combined_roi->
y;
503 for(
int y = 0; y < copy_h; y++)
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));
514 for(
int y = 0; y < process_stroke_mask->
height; y++)
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++)
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];
540 float **process_update_pixels,
541 size_t *process_update_capacity_pixels,
542 gboolean *cache_dirty, gboolean *process_patch_dirty,
544 const char *update_buffer_name)
550 if(process_patch->
width <= 0 || process_patch->
height <= 0 || base_patch->
width <= 0 || base_patch->
height <= 0)
552 if(process_combined_roi->
scale <= 1e-6f)
return TRUE;
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);
560 const gboolean has_dirty_bounds = process_dirty_rect && process_dirty_rect->
valid;
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);
573 if(src_x1 <= src_x0 || src_y1 <= src_y0)
return TRUE;
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;
579 process_update_capacity_pixels,
580 needed_pixels, update_buffer_name);
587 .width = process_patch->
width,
588 .height = process_patch->
height,
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),
603 for(
int yy = 0; yy < dst_h; yy++)
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));
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)
620 process_update_capacity_pixels,
626 dst_w, process_stroke_mask->
width);
628 for(
int yy = 0; yy < dst_h; yy++)
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));
636 if(cache_dirty) *cache_dirty =
TRUE;
637 *process_patch_dirty =
FALSE;
void dt_pixelpipe_cache_free_align_cache(struct dt_dev_pixelpipe_cache_t *cache, void **mem, const char *message)
#define __OMP_PARALLEL_FOR__(...)
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...
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.
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
struct dt_dev_pixelpipe_cache_t * pixelpipe_cache
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.