44 return fminf(fmaxf(
v, 0.0f), 1.0f);
48static inline float _lerpf(
const float a,
const float b,
const float t)
50 return a + (b - a) *
t;
56 if(strip_ratio <= 0.0f)
return 0.0f;
57 if(rho <= strip_ratio + 1e-6f)
return 2.0f * (float)G_PI;
58 return 4.0f * asinf(
_clamp01(strip_ratio / fmaxf(rho, 1e-6f)));
65 const float radius = fmaxf(0.5f, dab->
radius);
66 const float diameter = 2.0f * radius;
72 const float distance_percent)
74 if(count <= 0)
return 1.0f;
78 const float min_radius = fmaxf(0.5f, fminf(p_start->
radius, p_end->
radius));
84static inline float _paint_cubic_hermitef(
const float p0,
const float p1,
const float m0,
const float m1,
const float t)
86 const float t2 =
t *
t;
87 const float t3 = t2 *
t;
88 return (2.0f * t3 - 3.0f * t2 + 1.0f) * p0 + (t3 - 2.0f * t2 +
t) * m0
89 + (-2.0f * t3 + 3.0f * t2) * p1 + (t3 - t2) * m1;
97 const int count,
const float t)
103 const float seg_dx = p_end->
x - p_start->
x;
104 const float seg_dy = p_end->
y - p_start->
y;
105 const float seg_len = hypotf(seg_dx, seg_dy);
106 const float dir_x = (seg_len > 1e-6f) ? (seg_dx / seg_len) : p_start->
dir_x;
107 const float dir_y = (seg_len > 1e-6f) ? (seg_dy / seg_len) : p_start->
dir_y;
108 const float m1x = (count >= 3) ? 0.5f * (p_end->
x - p_prev->
x) : (p_end->
x - p_start->
x);
109 const float m1y = (count >= 3) ? 0.5f * (p_end->
y - p_prev->
y) : (p_end->
y - p_start->
y);
110 const float m2x = p_end->
x - p_start->
x;
111 const float m2y = p_end->
y - p_start->
y;
113 const dt_aligned_pixel_simd_t color_start = dt_load_simd(p_start->
color);
114 const dt_aligned_pixel_simd_t color_end = dt_load_simd(p_end->
color);
130 .color = { 0.0f, 0.0f, 0.0f, 0.0f },
136 .shape = (
t < 0.5f) ? p_start->
shape : p_end->
shape,
137 .mode = (
t < 0.5f) ? p_start->
mode : p_end->
mode,
148 const float sample_step)
153 if(
IS_NULL_PTR(dab) || !isfinite(sample_step))
return 1.0f;
154 const float support_radius = fmaxf(dab->
radius, 0.5f);
155 const float overlap_span = 2.0f * support_radius;
156 if(sample_step <= 1e-6f || overlap_span <= 1e-6f || sample_step >= overlap_span - 1e-6f)
return 1.0f;
158 const float half_strip = 0.5f * sample_step;
161 const float clamped_half_strip = fminf(half_strip, support_radius);
162 const float chord_half = sqrtf(fmaxf(support_radius * support_radius
163 - clamped_half_strip * clamped_half_strip, 0.0f));
164 const float strip_area = sample_step * chord_half
165 + 2.0f * support_radius * support_radius
166 * asinf(
_clamp01(clamped_half_strip / support_radius));
167 const float full_area = (float)G_PI * support_radius * support_radius;
168 return _clamp01(strip_area / fmaxf(full_area, 1e-6f));
171 const float strip_ratio =
_clamp01(half_strip / support_radius);
173 if(!isfinite(full_mass) || full_mass <= 1e-6f)
return 1.0f;
175 const float weight_samples = 32.f;
176 const float dr = 1.0f / weight_samples;
177 float strip_mass = 0.0f;
178 for(
int ir = 0; ir < weight_samples; ir++)
180 const float rho = ((float)ir + 0.5f) * dr;
182 if(!isfinite(profile) || profile <= 0.0f)
continue;
184 if(!isfinite(angle) || angle <= 0.0f)
continue;
185 strip_mass += angle * profile * rho * dr;
187 const float scale = strip_mass / full_mass;
188 return isfinite(scale) ?
_clamp01(scale) : 1.0f;
220 window[0] =
state->prev_raw_dab;
221 window[1] = *segment_start;
222 window[2] = *segment_end;
227 window[0] = *segment_start;
228 window[1] = *segment_end;
240 float *cumulative,
const int segments,
float *total_len)
245 cumulative[0] = 0.0f;
248 for(
int i = 1;
i <= segments;
i++)
250 const float t = (float)
i / (
float)segments;
252 *total_len += hypotf(cur.
x - prev.
x, cur.
y - prev.
y);
253 cumulative[
i] = *total_len;
262 const float target_norm,
263 const float *cumulative,
265 const float total_len)
268 if(
IS_NULL_PTR(cumulative) || segments <= 0 || total_len <= 1e-6f)
271 const float target_len =
_clamp01(target_norm) * total_len;
273 while(
k < segments && cumulative[
k + 1] < target_len)
k++;
275 const float l0 = cumulative[
k];
276 const float l1 = cumulative[
MIN(
k + 1, segments)];
277 const float span = fmaxf(
l1 - l0, 1e-6f);
278 const float local =
_clamp01((target_len - l0) / span);
279 const float t0 = (float)
k / (
float)segments;
280 const float t1 = (float)
MIN(
k + 1, segments) / (float)segments;
290 const float sample_spacing,
291 const float smoothing_percent,
296 if(!
state->history ||
state->history->len < 3)
return;
297 const float real_x = dab->
x;
298 const float real_y = dab->
y;
301 const int n = (int)
state->history->len;
305 const float real_radius = dab->
radius;
306 const float real_opacity = dab->
opacity;
307 const float real_flow = dab->
flow;
308 const float real_hardness = dab->
hardness;
309 const float real_sprinkles = dab->
sprinkles;
317 const float qx = 3.0f * p2->
x - 3.0f * p1->
x + p0->
x;
318 const float qy = 3.0f * p2->
y - 3.0f * p1->
y + p0->
y;
319 float dvx = qx - p2->
x;
320 float dvy = qy - p2->
y;
321 float dlen = hypotf(dvx, dvy);
324 dvx = real_x - p2->
x;
325 dvy = real_y - p2->
y;
326 dlen = hypotf(dvx, dvy);
328 const float step = fmaxf(sample_spacing, 1e-6f);
329 const float pred_x = (dlen > 1e-6f) ? (p2->
x + dvx * (step / dlen)) : real_x;
330 const float pred_y = (dlen > 1e-6f) ? (p2->
y + dvy * (step / dlen)) : real_y;
331 const float blend = 0.5f *
_clamp01(smoothing_percent);
332 const float pred_radius = fmaxf(0.5f, 3.0f * p2->
radius - 3.0f * p1->
radius + p0->
radius);
338 const float pred_sprinkle_coarseness
340 dab->
x =
_lerpf(dab->
x, pred_x, blend);
341 dab->
y =
_lerpf(dab->
y, pred_y, blend);
344 dab->
flow =
_lerpf(real_flow, pred_flow, blend);
361 const float rvx = real_x - prev->
x;
362 const float rvy = real_y - prev->
y;
363 const float svx = dab->
x - prev->
x;
364 const float svy = dab->
y - prev->
y;
365 const float real_dist = hypotf(rvx, rvy);
366 const float smooth_dist = hypotf(svx, svy);
367 const float min_safe = 0.5f * fmaxf(sample_spacing, 1e-6f);
368 const float dot = rvx * svx + rvy * svy;
369 if((smooth_dist < min_safe && real_dist > smooth_dist) || dot <= 0.0f)
375 if(layer_to_widget) layer_to_widget(user_data, dab->
x, dab->
y, &dab->
wx, &dab->
wy);
387 const float dx = dab->
x - prev->
x;
388 const float dy = dab->
y - prev->
y;
389 const float len = hypotf(dx, dy);
392 dab->
dir_x = dx / len;
393 dab->
dir_y = dy / len;
396 g_array_append_val(
state->history, *dab);
397 g_array_append_val(
state->pending_dabs, *dab);
411 const float sample_spacing,
419 const float target = fmaxf(sample_spacing, 1e-6f);
420 float dx = dab->
x - prev->
x;
421 float dy = dab->
y - prev->
y;
422 float d = hypotf(dx, dy);
425 float dir_x = dab->
dir_x;
426 float dir_y = dab->
dir_y;
427 float dir_len = hypotf(dir_x, dir_y);
432 dir_len = hypotf(dir_x, dir_y);
440 dx = dir_x / dir_len;
441 dy = dir_y / dir_len;
449 dab->
x = prev->
x + dx * target;
450 dab->
y = prev->
y + dy * target;
453 if(layer_to_widget) layer_to_widget(user_data, dab->
x, dab->
y, &dab->
wx, &dab->
wy);
462 if(
state->history) g_array_set_size(
state->history, 0);
463 if(
state->pending_dabs) g_array_set_size(
state->pending_dabs, 0);
464 if(
state->dab_window) g_array_set_size(
state->dab_window, 0);
469 state->stroke_arc_length = 0.0f;
470 state->sampled_arc_length = 0.0f;
471 state->distance_percent = 0.0f;
472 state->stroke_seed = 0u;
480 if(
state->raw_inputs) g_array_set_size(
state->raw_inputs, 0);
481 state->raw_input_cursor = 0;
492 &&
state->last_input_dab.stroke_batch != 0u
504 ^ (qx * 0x9e3779b185ebca87ull)
505 ^ (qy * 0xc2b2ae3d27d4eb4full);
513 state->last_input_dab = *dab;
520 state->sampled_arc_length = 0.0f;
530 if(!
state->history ||
state->history->len > 0)
return;
536 state->sampled_arc_length = 0.0f;
545 const float dx = dab->
x -
state->last_input_dab.x;
546 const float dy = dab->
y -
state->last_input_dab.y;
547 const float dir_len = hypotf(dx, dy);
550 state->last_input_dab.dir_x = dx / dir_len;
551 state->last_input_dab.dir_y = dy / dir_len;
574 state->distance_percent = distance_percent;
587 if(!
state->have_last_input_dab)
594 const float seg_len = hypotf(dab.
x - segment_start.
x, dab.
y - segment_start.
y);
595 const float prev_arc =
state->stroke_arc_length;
596 enum { arc_lut_segments = 24 };
597 float arc_lut[arc_lut_segments + 1] = { 0.0f };
598 float arc_total = 0.0f;
600 arc_lut, arc_lut_segments, &arc_total);
601 const float seg_arc = (arc_total > 1e-6f) ? arc_total : seg_len;
602 state->stroke_arc_length += seg_arc;
611 const float sample_spacing = segment_spacing;
612 const float target_arc =
state->sampled_arc_length + sample_spacing;
613 if(target_arc >
state->stroke_arc_length + 1e-6f)
break;
615 if(target_arc <= prev_arc + 1e-6f)
617 state->sampled_arc_length = target_arc;
621 const float t =
_clamp01((target_arc - prev_arc) / seg_arc);
624 arc_lut, arc_lut_segments, arc_total);
633 state->sampled_arc_length = target_arc;
637 state->prev_raw_dab = segment_start;
639 state->last_input_dab = dab;
646 g_array_append_val(
state->raw_inputs, *input);
654 if(
state->raw_input_cursor == 0)
return;
656 const guint processed =
state->raw_input_cursor;
657 const guint len =
state->raw_inputs->len;
659 g_array_set_size(
state->raw_inputs, 0);
661 g_array_remove_range(
state->raw_inputs, 0, processed);
663 state->raw_input_cursor = 0;
673 while(
state->raw_input_cursor <
state->raw_inputs->len)
678 state->raw_input_cursor++;
692 float pickup_x = 0.0f;
693 float pickup_y = 0.0f;
702 const float dx = previous ? (current->
x - previous->
x) : 0.0f;
703 const float dy = previous ? (current->
y - previous->
y) : 0.0f;
704 const float travel = hypotf(dx, dy);
705 if(travel <= 1e-6f)
return;
707 const float radius = fmaxf(current->
radius, 0.5f);
708 const float response = 1.0f - expf(-0.5f * travel / radius);
709 pickup_x =
_lerpf(pickup_x, current->
x, response);
710 pickup_y =
_lerpf(pickup_y, current->
y, response);
715 const float distance_percent,
727 GArray *
const history = runtime_private->
dab_window;
729 g_array_append_val(history, *dab);
731 const int total = (int)history->len;
732 const int count =
MIN(total, 3);
746 if(count == 1 && runtime_private)
760 const gboolean rasterized
771 const int bounds_w = runtime_private->
bounds.
se[0] - runtime_private->
bounds.
nw[0];
772 const int bounds_h = runtime_private->
bounds.
se[1] - runtime_private->
bounds.
nw[1];
774 "[drawlayer] paint raster mode=%d pos=%d spacing=%.3f alpha_scale=%.4f area=%dx%d ms=%.3f\n",
775 sample->
mode, sample->
stroke_pos, spacing, sample_opacity_scale, bounds_w, bounds_h,
780 "[drawlayer] paint raster mode=%d pos=%d spacing=%.3f alpha_scale=%.4f area=0x0 ms=%.3f\n",
781 sample->
mode, sample->
stroke_pos, spacing, sample_opacity_scale, 1000.0 * (t1 - t0));
784 if(history->len > 3) g_array_remove_range(history, 0, history->len - 3);
823 if((*state)->raw_inputs) g_array_free((*state)->raw_inputs,
TRUE);
824 dt_free((*state)->smudge_pixels);
832 state->smudge_pickup_x = 0.0f;
833 state->smudge_pickup_y = 0.0f;
835 if(
state->smudge_pixels &&
state->smudge_width > 0 &&
state->smudge_height > 0)
836 memset(
state->smudge_pixels, 0, (
size_t)
state->smudge_width *
state->smudge_height * 4 *
sizeof(
float));
842 state->stroke_seed = seed;
856 float *pixels = g_realloc(
state->smudge_pixels, (
size_t)
width *
height * 4 *
sizeof(
float));
858 state->smudge_pixels = pixels;
861 memset(
state->smudge_pixels, 0, (
size_t)
width *
height * 4 *
sizeof(
float));
893 const float x,
const float y,
const gboolean have_pickup)
896 state->smudge_pickup_x =
x;
897 state->smudge_pickup_y = y;
898 state->have_smudge_pickup = have_pickup;
904 const int origin_x,
const int origin_y,
909 const float support_radius = dab->
radius;
912 state->bounds.nw[0] =
MAX(0, (
int)floorf((dab->
x - support_radius) * scale) - origin_x);
913 state->bounds.nw[1] =
MAX(0, (
int)floorf((dab->
y - support_radius) * scale) - origin_y);
914 state->bounds.se[0] =
MIN(
width, (
int)ceilf((dab->
x + support_radius) * scale) - origin_x + 1);
915 state->bounds.se[1] =
MIN(
height, (
int)ceilf((dab->
y + support_radius) * scale) - origin_y + 1);
917 ||
state->bounds.se[1] <=
state->bounds.nw[1])
927 if(dab_rect->
se[0] <= dab_rect->
nw[0] || dab_rect->
se[1] <= dab_rect->
nw[1])
return;
953 if(add_rect->
se[0] <= add_rect->
nw[0] || add_rect->
se[1] <= add_rect->
nw[1])
return;
975 if(!have_damage)
return FALSE;
@ DT_DRAWLAYER_BRUSH_MODE_SMUDGE
@ DT_DRAWLAYER_BRUSH_SHAPE_GAUSSIAN
Inline brush profile and mass primitives shared by paint/brush code.
static float dt_drawlayer_brush_profile_eval(const dt_drawlayer_brush_dab_t *dab, const float norm2)
Evaluate normalized brush profile at squared normalized radius.
static float dt_drawlayer_brush_mass_primitive_eval(const dt_drawlayer_brush_dab_t *dab, const float u_in)
Evaluate radial mass primitive from center to normalized radius u_in.
return vector dt_simd_set1(valid ?(scaling+NORM_MIN) :NORM_MIN)
void dt_print(dt_debug_thread_t thread, const char *msg,...)
dt_store_simd(out, value)
static double dt_get_wtime(void)
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
gboolean dt_drawlayer_brush_rasterize(const dt_drawlayer_cache_patch_t *sample_patch, dt_drawlayer_cache_patch_t *patch, const float scale, const dt_drawlayer_brush_dab_t *dab, const float sample_opacity_scale, dt_drawlayer_cache_patch_t *stroke_mask, dt_drawlayer_paint_stroke_t *runtime_private)
Public dab rasterization entry point.
Patch/cache helpers for drawlayer process and preview buffers.
void dt_drawlayer_paint_path_state_reset(dt_drawlayer_paint_stroke_t *state)
Reset full stroke state including queued raw input events.
void dt_drawlayer_paint_runtime_state_destroy(dt_drawlayer_damaged_rect_t **state)
Destroy stroke-damage accumulator state and null pointer.
static float _paint_cubic_hermitef(const float p0, const float p1, const float m0, const float m1, const float t)
Cubic Hermite scalar interpolation helper.
static void _paint_process_one_raw_input(dt_drawlayer_paint_stroke_t *state, const dt_drawlayer_paint_raw_input_t *input, const dt_drawlayer_paint_callbacks_t *callbacks, void *user_data)
Process one raw input event into zero or more emitted dabs.
gboolean dt_drawlayer_paint_queue_raw_input(dt_drawlayer_paint_stroke_t *state, const dt_drawlayer_paint_raw_input_t *input)
Queue one raw input event (FIFO).
void dt_drawlayer_paint_runtime_private_destroy(dt_drawlayer_paint_stroke_t **state)
Destroy stroke runtime payload and null pointer.
static dt_drawlayer_brush_dab_t _sample_raw_segment_cubic_arclen(const dt_drawlayer_paint_stroke_t *state, const dt_drawlayer_brush_dab_t *segment_start, const dt_drawlayer_brush_dab_t *segment_end, const float target_norm, const float *cumulative, const int segments, const float total_len)
Sample a cubic segment at normalized arc length using LUT inversion.
static void _apply_quadratic_dab_smoothing(dt_drawlayer_paint_stroke_t *state, dt_drawlayer_brush_dab_t *dab, const float sample_spacing, const float smoothing_percent, const dt_drawlayer_paint_layer_to_widget_cb layer_to_widget, void *user_data)
Apply optional quadratic smoothing to one emitted dab.
static float _paint_segment_sample_spacing(const dt_drawlayer_brush_dab_t *dabs, const int count, const float distance_percent)
Resolve one segment spacing target from edge dab radii.
uint64_t dt_drawlayer_paint_runtime_get_stroke_seed(const dt_drawlayer_paint_stroke_t *state)
Get current deterministic stroke seed.
static void _enforce_dab_center_spacing(dt_drawlayer_paint_stroke_t *state, dt_drawlayer_brush_dab_t *dab, const float sample_spacing, const dt_drawlayer_paint_layer_to_widget_cb layer_to_widget, void *user_data)
Re-project current dab center to exact target spacing from previous dab.
void dt_drawlayer_paint_interpolate_path(dt_drawlayer_paint_stroke_t *state, const dt_drawlayer_paint_callbacks_t *callbacks, void *user_data)
Drain queued raw input events and append evenly spaced dabs to state->pending_dabs.
static void _flush_pending_initial_if_needed(dt_drawlayer_paint_stroke_t *state, const dt_drawlayer_brush_dab_t *dab)
Flush deferred initial dab before regular segment emission starts.
static void _paint_union_damage_rect(dt_drawlayer_damaged_rect_t *rect, const dt_drawlayer_damaged_rect_t *add_rect)
static gboolean _paint_input_starts_new_stroke(const dt_drawlayer_paint_stroke_t *state, const dt_drawlayer_paint_raw_input_t *input)
Test if current input denotes a new stroke boundary.
void dt_drawlayer_paint_runtime_set_stroke_seed(dt_drawlayer_paint_stroke_t *state, const uint64_t seed)
Set deterministic stroke seed for noise-derived effects.
float * dt_drawlayer_paint_runtime_smudge_pixels(dt_drawlayer_paint_stroke_t *state)
Get smudge carry buffer pointer (RGBA float).
void dt_drawlayer_paint_finalize_path(dt_drawlayer_paint_stroke_t *state)
Finalize stroke by force-emitting the pending first sample if needed.
static void _paint_reset_path_runtime_state(dt_drawlayer_paint_stroke_t *state)
Reset only path-generation state while keeping reusable allocations.
static float _clamp01(const float v)
Clamp scalar value to [0, 1].
static void _build_raw_segment_cubic_arclen_lut(const dt_drawlayer_paint_stroke_t *state, const dt_drawlayer_brush_dab_t *segment_start, const dt_drawlayer_brush_dab_t *segment_end, float *cumulative, const int segments, float *total_len)
Build arc-length lookup for the current cubic segment.
int dt_drawlayer_paint_runtime_smudge_height(const dt_drawlayer_paint_stroke_t *state)
Get smudge carry buffer height.
gboolean dt_drawlayer_paint_runtime_have_smudge_pickup(const dt_drawlayer_paint_stroke_t *state)
Query whether smudge pickup coordinates are initialized.
void dt_drawlayer_paint_runtime_private_reset(dt_drawlayer_paint_stroke_t *state)
Reset transient stroke runtime payload between strokes.
void dt_drawlayer_paint_runtime_set_smudge_pickup(dt_drawlayer_paint_stroke_t *state, const float x, const float y, const gboolean have_pickup)
Write smudge pickup coordinates and validity flag.
static gboolean _ensure_raw_inputs(dt_drawlayer_paint_stroke_t *state)
Lazily allocate raw-input queue storage for one stroke state.
static void _paint_compact_raw_input_queue(dt_drawlayer_paint_stroke_t *state)
void dt_drawlayer_paint_runtime_get_smudge_pickup(const dt_drawlayer_paint_stroke_t *state, float *x, float *y)
Read smudge pickup coordinates.
static void _emit_dab(dt_drawlayer_paint_stroke_t *state, dt_drawlayer_brush_dab_t *dab)
Emit one dab and append it to emitted-history tracking.
static float _lerpf(const float a, const float b, const float t)
Linear interpolation helper.
int dt_drawlayer_paint_runtime_smudge_width(const dt_drawlayer_paint_stroke_t *state)
Get smudge carry buffer width.
gboolean dt_drawlayer_paint_runtime_get_stroke_damage(const dt_drawlayer_damaged_rect_t *state, dt_drawlayer_damaged_rect_t *out_rect)
Read accumulated stroke damage rectangle.
static void _advance_smudge_pickup_state(dt_drawlayer_paint_stroke_t *state, const dt_drawlayer_brush_dab_t *current, const dt_drawlayer_brush_dab_t *previous)
gboolean dt_drawlayer_paint_rasterize_segment_to_buffer(const dt_drawlayer_brush_dab_t *dab, const float distance_percent, const dt_drawlayer_cache_patch_t *sample_patch, dt_drawlayer_cache_patch_t *patch, const float scale, dt_drawlayer_cache_patch_t *stroke_mask, dt_drawlayer_damaged_rect_t *runtime_state, dt_drawlayer_paint_stroke_t *runtime_private)
Replay one emitted dab segment into a float buffer through brush API.
static float _paint_stroke_sample_opacity_scale(const dt_drawlayer_brush_dab_t *dab, const float sample_step)
Compute per-sample opacity normalization from spacing.
static dt_drawlayer_brush_dab_t _paint_build_segment_window_sample(const dt_drawlayer_brush_dab_t *dabs, const int count, const float t)
Build one interpolated dab sample in the current segment window.
static uint64_t _paint_make_stroke_seed(const dt_drawlayer_paint_raw_input_t *input)
Build deterministic stroke seed from batch/time/coordinates.
void dt_drawlayer_paint_runtime_state_reset(dt_drawlayer_damaged_rect_t *state)
Reset stroke-damage accumulator to empty/invalid.
static float _paint_dab_sample_spacing(const dt_drawlayer_brush_dab_t *dab, const float distance_percent)
Resolve dab-to-dab center spacing from radius and distance percentage.
static void _emit_first_sample_if_needed(dt_drawlayer_paint_stroke_t *state, const dt_drawlayer_brush_dab_t *dab)
Optionally emit first sample immediately when stroke starts.
gboolean dt_drawlayer_paint_runtime_prepare_dab_context(dt_drawlayer_paint_stroke_t *state, const dt_drawlayer_brush_dab_t *dab, const int width, const int height, const int origin_x, const int origin_y, const float scale)
Compute current dab footprint bounds in target buffer coordinates.
static gboolean _ensure_pending_dabs(dt_drawlayer_paint_stroke_t *state)
Lazily allocate pending-dab batch storage for one stroke state.
void dt_drawlayer_paint_runtime_note_dab_damage(dt_drawlayer_damaged_rect_t *state, const dt_drawlayer_damaged_rect_t *dab_rect)
Merge one dab rectangle into an accumulator rectangle.
static void _freeze_emitted_dab_raster_state(dt_drawlayer_brush_dab_t *dab, const float sample_spacing)
Freeze raster-time normalization into one emitted dab record.
dt_drawlayer_damaged_rect_t * dt_drawlayer_paint_runtime_state_create(void)
Allocate zero-initialized stroke-damage accumulator state.
static float _paint_voronoi_strip_angle_measure(const float rho, const float strip_ratio)
Compute angular measure used by strip-based profile integration.
gboolean dt_drawlayer_paint_runtime_ensure_smudge_pixels(dt_drawlayer_paint_stroke_t *state, const int width, const int height)
Ensure smudge carry buffer allocation for given footprint dimensions.
static dt_drawlayer_brush_dab_t _sample_raw_segment_cubic_param(const dt_drawlayer_paint_stroke_t *state, const dt_drawlayer_brush_dab_t *segment_start, const dt_drawlayer_brush_dab_t *segment_end, const float t)
Sample the current raw segment at parametric position t.
gboolean dt_drawlayer_paint_merge_runtime_stroke_damage(dt_drawlayer_damaged_rect_t *path_state, dt_drawlayer_damaged_rect_t *target_rect)
Merge path-state damage into target rectangle and clear path-state accumulator.
dt_drawlayer_paint_stroke_t * dt_drawlayer_paint_runtime_private_create(void)
Allocate stroke runtime payload object used by paint+brush internals.
Stroke-level path sampling and runtime-state API for drawlayer.
@ DT_DRAWLAYER_PAINT_STROKE_MIDDLE
@ DT_DRAWLAYER_PAINT_STROKE_FIRST
gboolean(* dt_drawlayer_paint_layer_to_widget_cb)(void *user_data, float lx, float ly, float *wx, float *wy)
Convert layer-space coordinates back to widget-space (for HUD/preview alignment).
float *const restrict const size_t k
const float uint32_t state[4]
unsigned __int64 uint64_t
Fully resolved input dab descriptor.
float sample_opacity_scale
float sprinkle_coarseness
Generic float RGBA patch stored either in malloc memory or pixel cache.
Integer axis-aligned rectangle in buffer coordinates.
Callback bundle used by stroke processing entry points.
dt_drawlayer_paint_build_dab_cb build_dab
dt_drawlayer_paint_stroke_seed_cb on_stroke_seed
dt_drawlayer_paint_layer_to_widget_cb layer_to_widget
Mutable stroke runtime state owned by worker/backend code.
dt_drawlayer_damaged_rect_t bounds