32 if(bytes == 0)
return NULL;
48 if(*capacity_pixels < needed_pixels)
54 *capacity_pixels = needed_pixels;
63 memset(pixels, 0, pixel_count * 4 *
sizeof(
float));
74 void *buffer = patch->
pixels;
85 memset(patch, 0,
sizeof(*patch));
90 const size_t pixel_count,
const int width,
const int height,
91 const char *
name,
int *created_out)
99 if(!
IS_NULL_PTR(created_out)) *created_out = created;
115 patch->
pixels = (
float *)data;
130 const gboolean size_changed = (mask->width !=
width || mask->height !=
height || !mask->pixels);
139 mask->external_alloc =
TRUE;
156 memset(mask->pixels, 0, (
size_t)
width *
height *
sizeof(
float));
191 int *process_patch_padding,
194 if(process_patch_valid) *process_patch_valid =
FALSE;
195 if(process_patch_dirty) *process_patch_dirty =
FALSE;
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));
205 const int height,
const char *patch_buffer_name,
206 const char *mask_buffer_name)
212 || !process_patch->
pixels);
218 process_patch->
x = 0;
219 process_patch->
y = 0;
233 || !process_stroke_mask->
pixels)
238 process_stroke_mask->
x = 0;
239 process_stroke_mask->
y = 0;
243 if(!process_stroke_mask->
pixels)
245 process_stroke_mask->
width = 0;
246 process_stroke_mask->
height = 0;
255 process_stroke_mask->
width = 0;
256 process_stroke_mask->
height = 0;
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,
272 || src_w <= 0 || src_h <= 0)
274 if(!
IS_NULL_PTR(combined_roi)) memset(combined_roi, 0,
sizeof(*combined_roi));
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);
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;
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);
292 combined_roi->
scale = process_roi->
scale * fmaxf(fit, 1e-6f);
297 const int current_full_w,
const int current_full_h,
298 int *module_origin_x,
int *module_origin_y)
314 if(module_origin_x) *module_origin_x = origin_x;
315 if(module_origin_y) *module_origin_y = origin_y;
321 const int current_full_w,
322 const int current_full_h,
323 const int src_w,
const int src_h,
327 || src_w <= 0 || src_h <= 0)
329 if(!
IS_NULL_PTR(combined_roi)) memset(combined_roi, 0,
sizeof(*combined_roi));
333 int module_origin_x = 0;
334 int module_origin_y = 0;
336 &module_origin_x, &module_origin_y);
339 src_w, src_h, module_origin_x, module_origin_y, combined_roi);
344 const int process_patch_padding,
348 gboolean *direct_copy)
351 if(process_patch->
width <= 0 || process_patch->
height <= 0 || roi_out->
width <= 0 || roi_out->
height <= 0)
356 source_process_roi->
x = 0;
357 source_process_roi->
y = 0;
358 source_process_roi->
width = process_patch->
width;
360 source_process_roi->
scale = 1.0f;
363 const int process_pad =
MAX(process_patch_padding, 0);
366 blend_target_roi->
x = process_pad;
367 blend_target_roi->
y = process_pad;
370 blend_target_roi->
scale = 1.0f;
379 const int process_patch_padding,
382 const int layerbuf_width)
388 gboolean direct_copy =
FALSE;
390 &target_roi, &source_process_roi, &direct_copy))
395 memcpy(layerbuf, process_patch->
pixels, (
size_t)roi_out->
width * roi_out->
height * 4 *
sizeof(
float));
400 layerbuf_width, process_patch->
width);
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,
414 int *process_patch_padding,
416 const char *patch_buffer_name,
417 const char *mask_buffer_name)
420 || base_patch->
height <= 0 || patch_width <= 0 || patch_height <= 0 || combined_roi->scale <= 1e-6f)
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;
429 if(fabs(combined_roi->
scale - 1.0) <= 1e-6f)
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;
438 if(copy_w > 0 && copy_h > 0)
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);
447 memset(process_patch->
pixels, 0,
448 (
size_t)process_patch->
width * process_patch->
height * 4 *
sizeof(
float));
451 for(
int y = 0; y < copy_h; y++)
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));
460 memset(process_patch->
pixels, 0, (
size_t)process_patch->
width * process_patch->
height * 4 *
sizeof(
float));
468 .width = base_patch->
width,
469 .height = base_patch->
height,
477 if(process_patch_valid) *process_patch_valid =
TRUE;
478 if(process_patch_dirty) *process_patch_dirty =
FALSE;
482 && process_stroke_mask->
width == process_patch->
width
483 && process_stroke_mask->
height == process_patch->
height)
485 const gboolean have_base_mask = base_stroke_mask && base_stroke_mask->
pixels
486 && base_stroke_mask->
width > 0 && base_stroke_mask->
height > 0;
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)
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)
502 const int dst_x0 = src_x0 - combined_roi->
x;
503 const int dst_y0 = src_y0 - combined_roi->
y;
505 for(
int y = 0; y < copy_h; y++)
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));
516 for(
int y = 0; y < process_stroke_mask->
height; y++)
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++)
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];
542 float **process_update_pixels,
543 size_t *process_update_capacity_pixels,
544 gboolean *cache_dirty, gboolean *process_patch_dirty,
546 const char *update_buffer_name)
552 if(process_patch->
width <= 0 || process_patch->
height <= 0 || base_patch->
width <= 0 || base_patch->
height <= 0)
554 if(process_combined_roi->
scale <= 1e-6f)
return TRUE;
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);
562 const gboolean has_dirty_bounds = process_dirty_rect && process_dirty_rect->
valid;
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);
575 if(src_x1 <= src_x0 || src_y1 <= src_y0)
return TRUE;
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;
581 process_update_capacity_pixels,
582 needed_pixels, update_buffer_name);
589 .width = process_patch->
width,
590 .height = process_patch->
height,
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),
605 for(
int yy = 0; yy < dst_h; yy++)
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));
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)
622 process_update_capacity_pixels,
628 dst_w, process_stroke_mask->
width);
630 for(
int yy = 0; yy < dst_h; yy++)
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));
638 if(cache_dirty) *cache_dirty =
TRUE;
639 *process_patch_dirty =
FALSE;
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...
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
#define __OMP_PARALLEL_FOR__(...)
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
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.