41 return fminf(fmaxf(
v, 0.0f), 1.0f);
45static inline float _lerpf(
const float a,
const float b,
const float t)
47 return a + (b - a) *
t;
53 return (
float)
splitmix32(cell_seed ^ salt) / (float)UINT32_MAX;
59 const int cell_x = (int)floorf(
x);
60 const int cell_y = (int)floorf(y);
62 float weight_sum = 0.0f;
64 for(
int oy = -1; oy <= 1; oy++)
66 for(
int ox = -1; ox <= 1; ox++)
68 const int ix = cell_x + ox;
69 const int iy = cell_y + oy;
71 ^ ((
uint64_t)(uint32_t)ix * 0x9e3779b185ebca87ull)
72 ^ ((
uint64_t)(uint32_t)iy * 0xc2b2ae3d27d4eb4full);
76 const float dx =
x - ((float)ix + jitter_x);
77 const float dy = y - ((float)iy + jitter_y);
78 const float dist2 = dx * dx + dy * dy;
80 const float grain = fmaxf(0.0f, 1.0f - dist2 / (radius * radius));
81 const float shaped = grain * grain * (3.0f - 2.0f * grain);
82 accum += grain_gain * shaped;
83 weight_sum += grain_gain;
87 return (weight_sum > 1e-6f) ?
_clamp01(accum / weight_sum) : 0.0f;
93 const float c = 1.0f -
_clamp01(coarseness);
96 const float t = c * 2.0f;
103 const float t = (c - 0.5f) * 2.0f;
112 const float scale,
const float strength,
113 const float w0,
const float w1,
const float w2,
117 const float x = px * scale;
118 const float y = py * scale;
120 const float g1 = (
w1 > 1e-6f) ?
_cellular_grain_2d(seed1,
x * 1.93f + 4.7f, y * 1.93f - 2.9f) : 0.0f;
121 const float g2 = (
w2 > 1e-6f) ?
_cellular_grain_2d(seed2,
x * 3.71f - 6.2f, y * 3.71f + 8.4f) : 0.0f;
122 const float field = w0 * g0 +
w1 * g1 +
w2 * g2;
123 const float centered = 2.0f * field - 1.0f;
124 return fmaxf(0.0f, 1.0f +
strength * centered);
142 const float center_x,
const float center_y,
173 float noise_sum = 0.0f;
175 for(
int sy = -2; sy <= 2; sy++)
177 for(
int sx = -2; sx <= 2; sx++)
179 const float nx = 0.4f * (float)sx;
180 const float ny = 0.4f * (float)sy;
181 if(nx * nx + ny * ny > 1.0f)
continue;
182 const int pixel_x = (int)lrintf(center_x + nx * radius);
183 const int pixel_y = (int)lrintf(center_y + ny * radius);
194 const float mean_noise = noise_sum / (float)noise_count;
195 preview->gain = (mean_noise > 1e-6f) ? (1.0f / mean_noise) : 1.0f;
200 const float px,
const float py)
258 if(bounds->
se[0] <= bounds->
nw[0] || bounds->
se[1] <= bounds->
nw[1])
return FALSE;
260 const float dir_len = hypotf(dab->
dir_x, dab->
dir_y);
261 float sprinkle_w0 = 0.0f, sprinkle_w1 = 0.0f, sprinkle_w2 = 0.0f;
270 .scaled_radius = fmaxf(dab->
radius * scale, 0.5f),
271 .center_x = dab->
x * scale - (float)
origin_x,
272 .center_y = dab->
y * scale - (
float)
origin_y,
274 .tx = (dir_len > 1e-6f) ? (dab->
dir_x / dir_len) : 0.0f,
275 .ty = (dir_len > 1e-6f) ? (dab->
dir_y / dir_len) : 1.0f,
276 .alpha_noise_gain = 1.0f,
277 .sprinkle_coord_scale = 1.0f / fmaxf(scale, 1e-6f),
280 .sprinkle_w0 = sprinkle_w0,
281 .sprinkle_w1 = sprinkle_w1,
282 .sprinkle_w2 = sprinkle_w2,
283 .sprinkle_seed0 = sprinkle_seed0,
284 .sprinkle_seed1 = sprinkle_seed0 ^ 0xbf58476d1ce4e5b9ull,
285 .sprinkle_seed2 = sprinkle_seed0 ^ 0x94d049bb133111ebull,
287 .have_sprinkles = (dab->
sprinkles > 1e-6f),
289 view->inv_radius = 1.0f /
view->scaled_radius;
300 if(!stroke)
return FALSE;
310 const int pixel_x,
const int pixel_y)
312 float alpha_noise = 1.0f;
315 const float layer_x = ((float)pixel_x + 0.5f) *
view->sprinkle_coord_scale;
316 const float layer_y = ((float)pixel_y + 0.5f) *
view->sprinkle_coord_scale;
318 view->sprinkle_scale,
319 view->sprinkle_strength,
323 view->sprinkle_seed0,
324 view->sprinkle_seed1,
325 view->sprinkle_seed2);
336 float noise_sum = 0.0f;
338 for(
int sy = -2; sy <= 2; sy++)
340 for(
int sx = -2; sx <= 2; sx++)
342 const float nx = 0.4f * (float)sx;
343 const float ny = 0.4f * (float)sy;
344 if(nx * nx + ny * ny > 1.0f)
continue;
346 const int pixel_x = (int)lrintf(
view->sample_origin_x +
view->center_x + nx *
view->scaled_radius);
347 const int pixel_y = (int)lrintf(
view->sample_origin_y +
view->center_y + ny *
view->scaled_radius);
353 if(noise_count <= 0)
return 1.0f;
354 const float mean_noise = noise_sum / (float)noise_count;
355 return (mean_noise > 1e-6f) ? (1.0f / mean_noise) : 1.0f;
366 const float sample_opacity_scale,
const float profile,
const float brush_alpha,
367 const float old_alpha,
const float stroke_old_alpha,
368 const gboolean have_stroke_alpha)
371 const float opacity_scale
372 = isfinite(sample_opacity_scale) ? CLAMP(sample_opacity_scale, 1e-6f, 1.0f) : 1.0f;
379 const float normalized = 1.0f - powf(fmaxf(1.0f - brush_alpha, 0.0f), opacity_scale);
383 const float flow_ref_alpha = have_stroke_alpha ? stroke_old_alpha
389 const float stroke_cap =
_clamp01(opacity);
390 const float remaining_to_cap = fmaxf(stroke_cap - flow_ref_alpha, 0.0f);
391 const float capped_alpha = fminf(
_clamp01(brush_alpha),
392 remaining_to_cap / fmaxf(1.0f - flow_ref_alpha, 1e-6f));
400 if(flow <= 0.0f)
return _clamp01(capped_alpha);
402 const float accum_alpha = 1.0f - powf(fmaxf(1.0f - brush_alpha, 0.0f), opacity_scale);
414 const float sample_opacity_scale,
415 float *stroke_mask,
const int stroke_mask_width,
416 const int stroke_mask_height,
417 const int x,
const int y,
const float old_alpha,
423 const float dy = ((float)y + 0.5f -
view->center_y) *
view->inv_radius;
424 const float dx = ((float)
x + 0.5f -
view->center_x) *
view->inv_radius;
425 const float norm2 = dx * dx + dy * dy;
430 view->sample_origin_x +
x,
431 view->sample_origin_y + y)
432 *
view->alpha_noise_gain);
440 if(
view->use_stroke_mask)
442 pixel_eval->
stroke_alpha = stroke_mask + (size_t)y * stroke_mask_width +
x;
458 const int height,
const int source_origin_x,
const int source_origin_y,
459 const int patch_origin_x,
const int patch_origin_y,
463 float blur_weight_sum = 0.0f;
466 for(
int y =
view->bounds.nw[1]; y <
view->bounds.se[1]; y++)
468 const float dy = ((float)y + 0.5f -
view->center_y) *
view->inv_radius;
469 const float dy2 = dy * dy;
470 for(
int x =
view->bounds.nw[0];
x <
view->bounds.se[0];
x++)
472 const float dx = ((float)
x + 0.5f -
view->center_x) *
view->inv_radius;
474 if(blur_weight <= 0.0f)
continue;
476 const int source_x =
x + patch_origin_x - source_origin_x;
477 const int source_y = y + patch_origin_y - source_origin_y;
478 if(source_x < 0 || source_y < 0 || source_x >=
width || source_y >=
height)
continue;
479 const float *pixel = buffer + 4 * ((size_t)source_y *
width + source_x);
480 blur_sum += dt_load_simd(pixel) *
dt_simd_set1(blur_weight);
481 blur_weight_sum += blur_weight;
485 if(blur_weight_sum <= 1e-8f)
return FALSE;
486 *blur_px = blur_sum *
dt_simd_set1(1.0f / blur_weight_sum);
494 guint32 h = (guint32)(
x * 73856093u) ^ (guint32)(y * 19349663u) ^ (guint32)(lane * 83492791u);
498 return ((h & 0xffffu) / 32767.5f) - 1.0f;
506_sample_rgba_float_bilinear(
const float *buffer,
const int width,
const int height,
const float x,
511 const float fx = CLAMP(
x, 0.0f, (
float)(
width - 1));
512 const float fy = CLAMP(y, 0.0f, (
float)(
height - 1));
513 const int x0 = (int)floorf(
fx);
514 const int y0 = (int)floorf(fy);
515 const int x1 =
MIN(
width - 1, x0 + 1);
517 const float tx =
fx - x0;
518 const float ty = fy - y0;
520 const float *p00 = buffer + 4 * ((size_t)y0 *
width + x0);
521 const float *p10 = buffer + 4 * ((size_t)y0 *
width + x1);
522 const float *p01 = buffer + 4 * ((size_t)y1 *
width + x0);
523 const float *p11 = buffer + 4 * ((size_t)y1 *
width + x1);
524 const dt_aligned_pixel_simd_t p00v = dt_load_simd(p00);
525 const dt_aligned_pixel_simd_t p10v = dt_load_simd(p10);
526 const dt_aligned_pixel_simd_t p01v = dt_load_simd(p01);
527 const dt_aligned_pixel_simd_t p11v = dt_load_simd(p11);
530 const dt_aligned_pixel_simd_t av = p00v + (p10v - p00v) * txv;
531 const dt_aligned_pixel_simd_t bv = p01v + (p11v - p01v) * txv;
532 return av + (bv - av) * tyv;
540static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
541_sample_smudge_source_float(
const float *buffer,
const int width,
const int height,
const float sx,
542 const float sy,
const float motion_dx,
const float motion_dy,
543 const int jitter_x,
const int jitter_y)
548 float dir_x = motion_dx;
549 float dir_y = motion_dy;
550 const float motion = hypotf(dir_x, dir_y);
562 const float perp_x = -dir_y;
563 const float perp_y = dir_x;
568 const float taps[7][3] = {
569 { 0.00f, jitter, 0.24f },
570 { -trail, 0.25f + jitter, 0.18f },
571 { -0.45f, -0.35f + jitter, 0.15f },
572 { -0.15f, side + jitter, 0.11f },
573 { -0.15f, -side + jitter, 0.11f },
574 { 0.25f, 0.45f * side + jitter, 0.11f },
575 { 0.25f, -0.45f * side + jitter, 0.10f },
578 float weight_sum = 0.0f;
579 for(
int i = 0;
i < 7;
i++)
581 const float px = sx + dir_x * taps[
i][0] + perp_x * taps[
i][1];
582 const float py = sy + dir_y * taps[
i][0] + perp_y * taps[
i][1];
583 const float w = taps[
i][2];
588 if(weight_sum > 1e-8f)
596 const float carry =
_clamp01(carried_alpha);
597 const float base =
_clamp01(opacity);
598 const float influence = base + (1.0f - base) * carry;
599 return _clamp01(src_alpha * influence);
607 const int source_height,
const int source_origin_x,
608 const int source_origin_y,
611 const float scale,
const int patch_origin_x,
612 const int patch_origin_y,
const int x,
const int y,
613 const float src_alpha,
614 const dt_aligned_pixel_simd_t old_px)
617 const float source_x_offset = (float)(patch_origin_x - source_origin_x);
618 const float source_y_offset = (float)(patch_origin_y - source_origin_y);
619 const float center_abs_x =
view->center_x + (float)patch_origin_x;
620 const float center_abs_y =
view->center_y + (float)patch_origin_y;
621 float sample_x = (float)
x + source_x_offset;
622 float sample_y = (float)y + source_y_offset;
623 float motion_dx = 0.0f;
624 float motion_dy = 0.0f;
631 float pickup_center_x = 0.0f;
632 float pickup_center_y = 0.0f;
634 pickup_center_x *= scale;
635 pickup_center_y *= scale;
636 sample_x += pickup_center_x - center_abs_x;
637 sample_y += pickup_center_y - center_abs_y;
638 motion_dx = center_abs_x - pickup_center_x;
639 motion_dy = center_abs_y - pickup_center_y;
642 const dt_aligned_pixel_simd_t sampled_px
643 = _sample_smudge_source_float(source_buffer, source_width, source_height, sample_x, sample_y,
644 motion_dx, motion_dy,
645 x -
view->bounds.nw[0], y -
view->bounds.nw[1]);
647 if(
IS_NULL_PTR(smudge_pixels) || smudge_width <= 0)
return old_px;
648 float *carry = smudge_pixels + 4 * ((size_t)(y -
view->bounds.nw[1]) * smudge_width + (
x -
view->bounds.nw[0]));
649 const dt_aligned_pixel_simd_t carried_px = dt_load_simd(carry);
651 const float carried_alpha =
_clamp01(carry[3]);
653 const float inv_alpha = 1.0f - deposit_alpha;
655 const dt_aligned_pixel_simd_t out_px = carried_px *
dt_simd_set1(deposit_alpha)
657 const dt_aligned_pixel_simd_t next_carry = carried_px
658 + (sampled_px - carried_px) *
dt_simd_set1(pickup_blend);
670 const float sample_opacity_scale,
682 float *
const buffer = patch->
pixels;
688 = (sample_patch && sample_patch->
pixels) ? sample_patch : patch;
689 const float *
const source_buffer = source_patch->
pixels;
690 const int source_width = source_patch->
width;
691 const int source_height = source_patch->
height;
692 const int source_origin_x = source_patch->
x;
693 const int source_origin_y = source_patch->
y;
694 float *
const stroke_mask_pixels = stroke_mask ? stroke_mask->
pixels : NULL;
695 const int stroke_mask_width = stroke_mask ? stroke_mask->
width : 0;
696 const int stroke_mask_height = stroke_mask ? stroke_mask->
height : 0;
713 switch(
view.dab->mode)
723 view.bounds.se[1] -
view.bounds.nw[1]))
734 for(
int y =
view.bounds.nw[1]; y <
view.bounds.se[1]; y++)
736 for(
int x =
view.bounds.nw[0];
x <
view.bounds.se[0];
x++)
738 float *pixel = buffer + 4 * ((size_t)y *
width +
x);
739 const float old_alpha =
_clamp01(pixel[3]);
740 const dt_aligned_pixel_simd_t old_px = (old_alpha > 1e-8f) ? dt_load_simd(pixel) :
dt_simd_set1(0.0f);
743 stroke_mask_pixels, stroke_mask_width, stroke_mask_height,
744 x, y, old_alpha, &pixel_eval))
747 const dt_aligned_pixel_simd_t out_px
749 source_origin_y, runtime_private, &
view,
765#if defined(_OPENMP) && !OUTER_LOOP
766#pragma omp parallel for default(firstprivate) collapse(2)
768 for(
int y =
view.bounds.nw[1]; y <
view.bounds.se[1]; y++)
770 for(
int x =
view.bounds.nw[0];
x <
view.bounds.se[0];
x++)
772 float *pixel = buffer + 4 * ((size_t)y *
width +
x);
773 const float old_alpha =
_clamp01(pixel[3]);
774 const dt_aligned_pixel_simd_t old_px = (old_alpha > 1e-8f) ? dt_load_simd(pixel) :
dt_simd_set1(0.0f);
777 stroke_mask_pixels, stroke_mask_width, stroke_mask_height,
778 x, y, old_alpha, &pixel_eval))
781 dt_aligned_pixel_simd_t out_px;
783 switch(
view.dab->mode)
786 out_px = old_px * inv_alpha;
852 const int x,
const int y)
854 const float dy = ((float)y + 0.5f -
view->center_y) *
view->inv_radius;
855 const float dx = ((float)
x + 0.5f -
view->center_x) *
view->inv_radius;
857 if(profile <= 0.0f)
return 0.0f;
860 view->sample_origin_x +
x,
861 view->sample_origin_y + y)
862 *
view->alpha_noise_gain);
863 return _clamp01(
view->dab->opacity * profile * alpha_noise);
868 const int x,
const int y,
const float raw_noise)
870 const float dy = ((float)y + 0.5f -
view->center_y) *
view->inv_radius;
871 const float dx = ((float)
x + 0.5f -
view->center_x) *
view->inv_radius;
873 if(profile <= 0.0f)
return 0.0f;
875 const float alpha_noise = fmaxf(0.0f, raw_noise *
view->alpha_noise_gain);
876 return _clamp01(
view->dab->opacity * profile * alpha_noise);
892 for(guint
i = 1;
i < count;
i++)
898 if(
d->color[0] != first->
color[0] ||
d->color[1] != first->
color[1]
899 ||
d->color[2] != first->
color[2] ||
d->color[3] != first->
color[3])
912 for(
int c = 0; c < 4; c++)
out->color[c] = first->
color[c];
920 float *
const transmittance,
921 float *
const noise_scratch,
947 for(guint
i = 0;
i < batch->
count;
i++)
955 bounds.
nw[0] =
MAX(0, (
int)floorf((dab.
x - dab.
radius) * scale) - patch->
x);
956 bounds.
nw[1] =
MAX(0, (
int)floorf((dab.
y - dab.
radius) * scale) - patch->
y);
959 if(bounds.
se[0] <= bounds.
nw[0] || bounds.
se[1] <= bounds.
nw[1])
continue;
961 prepared[live] = dab;
969 if(live == 0 || !
bbox.valid)
976 const int y0 =
bbox.nw[1];
977 const int y1 =
bbox.se[1];
978 const int x0 =
bbox.nw[0];
979 const int x1 =
bbox.se[0];
991#pragma omp parallel for default(firstprivate) schedule(static)
993 for(
int y = y0; y < y1; y++)
995 float *
const nrow = noise_scratch + (size_t)y *
width;
996 for(
int x = x0;
x < x1;
x++)
998 views[0].sample_origin_y + y);
1006#pragma omp parallel for default(firstprivate) schedule(static)
1008 for(
int y = y0; y < y1; y++)
1010 float *
const row = transmittance + (size_t)y *
width;
1011 const float *
const nrow = shared_noise ? (noise_scratch + (size_t)y *
width) : NULL;
1012 for(
int x = x0;
x < x1;
x++)
row[
x] = 1.0f;
1014 for(guint
i = 0;
i < live;
i++)
1017 if(y < view->bounds.nw[1] || y >=
view->bounds.se[1])
continue;
1019 for(
int x =
view->bounds.nw[0];
x <
view->bounds.se[0];
x++)
1021 const float brush_alpha
1023 if(brush_alpha <= 0.0f)
continue;
1024 row[
x] *= (1.0f - brush_alpha);
1031 const float cap = batch->
cap;
1034#pragma omp parallel for default(firstprivate) schedule(static)
1036 for(
int y = y0; y < y1; y++)
1038 const float *
const trow = transmittance + (size_t)y *
width;
1039 float *
const mrow = stroke_mask->
pixels + (size_t)y *
width;
1040 float *
const prow = patch->
pixels + 4 * ((size_t)y *
width);
1042 for(
int x = x0;
x < x1;
x++)
1044 const float t = trow[
x];
1045 if(
t >= 1.0f)
continue;
1047 const float stroke_old_alpha =
_clamp01(mrow[
x]);
1048 float stroke_alpha = 1.0f -
t * (1.0f - stroke_old_alpha);
1049 if(stroke_alpha > cap) stroke_alpha = cap;
1054 const float src_alpha
1055 =
_clamp01((stroke_alpha - stroke_old_alpha) / fmaxf(1.0f - stroke_old_alpha, 1e-6f));
1056 if(src_alpha <= 0.0f)
continue;
1058 float *
const pixel = prow + 4 *
x;
1059 const float old_alpha =
_clamp01(pixel[3]);
1060 const dt_aligned_pixel_simd_t old_px = (old_alpha > 1e-8f) ? dt_load_simd(pixel) :
dt_simd_set1(0.0f);
1061 const dt_aligned_pixel_simd_t inv_alpha =
dt_simd_set1(1.0f - src_alpha);
1062 const dt_aligned_pixel_simd_t out_px
1063 = erase ? (old_px * inv_alpha)
1066 mrow[
x] = stroke_alpha;
1082 const int width,
const int height,
const int stride,
1083 const float center_x,
const float center_y,
1084 const float opacity_multiplier)
1089 memset(argb, 0, (
size_t)stride *
height);
1096 const float radius = fmaxf(
preview.radius, 0.5f);
1097 const float inv_radius = 1.0f / radius;
1098 const int x0 = (int)fmaxf(0.0f, floorf(center_x - radius));
1099 const int y0 = (int)fmaxf(0.0f, floorf(center_y - radius));
1100 const int x1 = (int)fminf((
float)
width, ceilf(center_x + radius) + 1.0f);
1101 const int y1 = (int)fminf((
float)
height, ceilf(center_y + radius) + 1.0f);
1109 for(
int y = y0; y < y1; y++)
1111 const float dy = ((float)y + 0.5f - center_y) * inv_radius;
1112 const float dy2 = dy * dy;
1113 for(
int x = x0;
x < x1;
x++)
1115 const float dx = ((float)
x + 0.5f - center_x) * inv_radius;
1117 if(profile <= 0.0f)
continue;
1120 const float alpha =
_clamp01(
preview.opacity * profile * alpha_noise);
1121 if(alpha <= 0.0f)
continue;
1123 uint8_t *pixel = argb + (size_t)y * stride + 4 *
x;
1124 pixel[0] = (uint8_t)fminf(fmaxf(roundf(255.0f *
_clamp01(disp_b * alpha)), 0.0f), 255.0f);
1125 pixel[1] = (uint8_t)fminf(fmaxf(roundf(255.0f *
_clamp01(disp_g * alpha)), 0.0f), 255.0f);
1126 pixel[2] = (uint8_t)fminf(fmaxf(roundf(255.0f *
_clamp01(disp_r * alpha)), 0.0f), 255.0f);
1127 pixel[3] = (uint8_t)fminf(fmaxf(roundf(255.0f * alpha), 0.0f), 255.0f);
1136 const float center_x,
const float center_y,
1137 const float opacity_multiplier,
1138 const float background_rgb[3])
1142 const float bg_r = background_rgb ?
_clamp01(background_rgb[0]) : 1.0f;
1143 const float bg_g = background_rgb ?
_clamp01(background_rgb[1]) : 1.0f;
1144 const float bg_b = background_rgb ?
_clamp01(background_rgb[2]) : 1.0f;
1147 rgba[4 *
i + 0] = bg_r;
1148 rgba[4 *
i + 1] = bg_g;
1149 rgba[4 *
i + 2] = bg_b;
1150 rgba[4 *
i + 3] = 1.0f;
1159 const float radius = fmaxf(
preview.radius, 0.5f);
1160 const float inv_radius = 1.0f / radius;
1161 const int x0 = (int)fmaxf(0.0f, floorf(center_x - radius));
1162 const int y0 = (int)fmaxf(0.0f, floorf(center_y - radius));
1163 const int x1 = (int)fminf((
float)
width, ceilf(center_x + radius) + 1.0f);
1164 const int y1 = (int)fminf((
float)
height, ceilf(center_y + radius) + 1.0f);
1172 for(
int y = y0; y < y1; y++)
1174 const float dy = ((float)y + 0.5f - center_y) * inv_radius;
1175 const float dy2 = dy * dy;
1176 for(
int x = x0;
x < x1;
x++)
1178 const float dx = ((float)
x + 0.5f - center_x) * inv_radius;
1180 if(profile <= 0.0f)
continue;
1183 const float alpha =
_clamp01(
preview.opacity * profile * alpha_noise);
1184 if(alpha <= 0.0f)
continue;
1186 float *pixel = rgba + 4 * ((size_t)y *
width +
x);
1187 const float inv_alpha = 1.0f - alpha;
1188 pixel[0] = src_r * alpha + pixel[0] * inv_alpha;
1189 pixel[1] = src_g * alpha + pixel[1] * inv_alpha;
1190 pixel[2] = src_b * alpha + pixel[2] * inv_alpha;
const char ** views(dt_lib_module_t *self)
Dab-level brush rasterization API for drawlayer.
@ DT_DRAWLAYER_BRUSH_MODE_ERASE
@ DT_DRAWLAYER_BRUSH_MODE_PAINT
@ DT_DRAWLAYER_BRUSH_MODE_BLUR
@ DT_DRAWLAYER_BRUSH_MODE_SMUDGE
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_profile_eval_fast(const dt_drawlayer_brush_profile_const_t *pc, const float norm2)
Evaluate the profile against pre-resolved dab constants.
static void dt_drawlayer_brush_profile_prepare(const dt_drawlayer_brush_dab_t *dab, dt_drawlayer_brush_profile_const_t *out)
Resolve the per-dab half of the profile once.
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
return vector dt_simd_set1(valid ?(scaling+NORM_MIN) :NORM_MIN)
const dt_colormatrix_t dt_aligned_pixel_t out
static float strength(float value, float strength)
static unsigned int splitmix32(const unsigned long seed)
GtkWidget * preview
what the selected row actually captures
GtkWidget * view
the tree view
static gboolean _brush_runtime_view_from_state(const dt_drawlayer_paint_stroke_t *stroke, const dt_drawlayer_brush_dab_t *dab, const int origin_x, const int origin_y, const float scale, dt_drawlayer_brush_runtime_view_t *view)
static float _cellular_grain_2d(const uint64_t seed, const float x, const float y)
Grain-like round cellular field. Peaks at grain centers, falls off radially.
gboolean dt_drawlayer_brush_rasterize_dab_rgbaf(const dt_drawlayer_brush_dab_t *dab, float *rgba, const int width, const int height, const float center_x, const float center_y, const float opacity_multiplier, const float background_rgb[3])
Rasterize a single dab preview in linear float RGBA over an opaque background.
static float _sample_alpha_noise_raw(const dt_drawlayer_brush_dab_t *dab, const dt_drawlayer_brush_runtime_view_t *view, const int pixel_x, const int pixel_y)
Resolve multiplicative alpha noise at one pixel.
static void _prepare_sprinkle_preview(const dt_drawlayer_brush_dab_t *dab, const float center_x, const float center_y, const float radius, dt_drawlayer_sprinkle_preview_t *preview)
static gboolean _prepare_blur_context(dt_aligned_pixel_simd_t *blur_px, const float *buffer, const int width, const int height, const int source_origin_x, const int source_origin_y, const int patch_origin_x, const int patch_origin_y, const dt_drawlayer_brush_runtime_view_t *view)
Build blur gather color for current dab footprint.
static float _smudge_hash_signed(const int x, const int y, const int lane)
Stable signed pseudo-random helper in [-1,1].
static float _clamp01(const float v)
Clamp scalar to [0,1].
static gboolean _brush_runtime_view_from_bounds(const dt_drawlayer_damaged_rect_t *bounds, const dt_drawlayer_brush_dab_t *dab, const int origin_x, const int origin_y, const float scale, dt_drawlayer_brush_runtime_view_t *view)
Build immutable per-dab raster view from stroke runtime state.
static float _smudge_deposit_alpha(const float src_alpha, const float carried_alpha, const float opacity)
Resolve effective smudge deposit alpha for one pixel.
static float _stroke_flow_alpha(const dt_drawlayer_brush_dab_t *dab, const float opacity, const float flow, const float sample_opacity_scale, const float profile, const float brush_alpha, const float old_alpha, const float stroke_old_alpha, const gboolean have_stroke_alpha)
Compute per-pixel source alpha from opacity/flow model.
static float _brush_alpha_at(const dt_drawlayer_brush_runtime_view_t *const view, const int x, const int y)
Per-pixel brush alpha, the part of the pixel context that owes nothing to the destination.
gboolean dt_drawlayer_brush_rasterize_dab_argb8(const dt_drawlayer_brush_dab_t *dab, uint8_t *argb, const int width, const int height, const int stride, const float center_x, const float center_y, const float opacity_multiplier)
Render one dab to 8-bit ARGB surface for GUI cursor preview.
static void _sprinkle_octave_weights(const float coarseness, float *w0, float *w1, float *w2)
Resolve octave weights from coarseness control.
static float _sprinkle_noise_at_pixel_precomputed(const float px, const float py, const float scale, const float strength, const float w0, const float w1, const float w2, const uint64_t seed0, const uint64_t seed1, const uint64_t seed2)
Evaluate sprinkle modulation from precomputed dab constants.
static float _estimate_alpha_noise_gain(const dt_drawlayer_brush_dab_t *dab, const dt_drawlayer_brush_runtime_view_t *view)
Estimate per-dab texture gain so noise preserves average opacity across samples.
static float _sample_sprinkle_preview(const dt_drawlayer_sprinkle_preview_t *preview, const float px, const float py)
static float _cell_hash01_from_seed(const uint64_t cell_seed, const uint64_t salt)
Stable scalar hash in [0,1] from precomputed cell seed.
static float _lerpf(const float a, const float b, const float t)
Linear interpolation helper.
gboolean dt_drawlayer_brush_batch_is_uniform(const dt_drawlayer_brush_dab_t *dabs, const guint count, dt_drawlayer_brush_batch_t *out)
Decide whether a run of dabs can take the batch path, and describe it if so.
static dt_aligned_pixel_simd_t _apply_smudge_stroke_mode(const float *source_buffer, const int source_width, const int source_height, const int source_origin_x, const int source_origin_y, dt_drawlayer_paint_stroke_t *runtime_private, const dt_drawlayer_brush_runtime_view_t *view, const float scale, const int patch_origin_x, const int patch_origin_y, const int x, const int y, const float src_alpha, const dt_aligned_pixel_simd_t old_px)
Apply smudge mode for one pixel and update carried sample.
static gboolean _prepare_analytic_pixel_context(const dt_drawlayer_brush_runtime_view_t *view, const float sample_opacity_scale, float *stroke_mask, const int stroke_mask_width, const int stroke_mask_height, const int x, const int y, const float old_alpha, dt_drawlayer_brush_pixel_eval_t *pixel_eval)
Compute full analytic per-pixel brush context.
static float _brush_alpha_at_noise(const dt_drawlayer_brush_runtime_view_t *const view, const int x, const int y, const float raw_noise)
As _brush_alpha_at, with the shared sprinkle field already sampled.
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.
gboolean dt_drawlayer_brush_rasterize_batch(const dt_drawlayer_brush_batch_t *batch, dt_drawlayer_cache_patch_t *patch, const float scale, dt_drawlayer_cache_patch_t *stroke_mask, float *const transmittance, float *const noise_scratch, dt_drawlayer_damaged_rect_t *batch_damage)
Rasterize a whole batch in two passes: accumulate transmittance, then composite once.
Patch/cache helpers for drawlayer process and preview buffers.
float * dt_drawlayer_paint_runtime_smudge_pixels(dt_drawlayer_paint_stroke_t *state)
Get smudge carry buffer pointer (RGBA float).
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_get_smudge_pickup(const dt_drawlayer_paint_stroke_t *state, float *x, float *y)
Read smudge pickup coordinates.
int dt_drawlayer_paint_runtime_smudge_width(const dt_drawlayer_paint_stroke_t *state)
Get smudge carry buffer width.
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.
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.
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.
Stroke-level path sampling and runtime-state API for drawlayer.
static const dt_dev_pixelpipe_t const dt_dev_pixelpipe_iop_t int int int int * origin_y
static const dt_dev_pixelpipe_t const dt_dev_pixelpipe_iop_t int int int * origin_x
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
dt_store_simd(out, value)
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Apply one channel's tone curve to each of the three colour channels, or pass the channel through unto...
unsigned __int64 uint64_t
A run of dabs that share everything the batch composite needs to be a single step.
const dt_drawlayer_brush_dab_t * dabs
Fully resolved input dab descriptor.
float sprinkle_coarseness
The part of the profile that depends on the dab and not on the pixel.
dt_drawlayer_damaged_rect_t bounds
const dt_drawlayer_brush_dab_t * dab
float sprinkle_coord_scale
dt_drawlayer_brush_profile_const_t profile
Generic float RGBA patch stored either in malloc memory or pixel cache.
Integer axis-aligned rectangle in buffer coordinates.
Mutable stroke runtime state owned by worker/backend code.
dt_drawlayer_damaged_rect_t bounds