48#define DT_CRYSTGRAIN_LAYER_KERNELS 16
53#define DT_CRYSTGRAIN_MIN_RADIUS 1e-3f
157 return _(
"Photographic grain");
162 return dt_iop_set_description(self, _(
"simulate photographic grain from stacked silver-halide crystal layers"),
164 _(
"non-linear, RGB, scene-referred"),
165 _(
"non-linear, RGB"),
166 _(
"non-linear, RGB, scene-referred"));
192 p.size_stddev = 0.25f;
193 p.layer_capture = 0.0f;
194 p.channel_correlation = 67.0f;
195 p.colorspace_saturation = 67.0f;
202 p.size_stddev = 0.25f;
203 p.layer_capture = 0.0f;
204 p.channel_correlation = 67.0f;
205 p.colorspace_saturation = 67.0f;
210 const int new_version)
212 if((old_version == 1 || old_version == 8) && new_version == 9)
230 while(*s) h = 33 * h ^ (
unsigned int)*s++;
253 return sqrtf(-2.0f * logf(u1)) * cosf(2.0f *
M_PI_F *
u2);
262 if(
max <= 1)
return 0;
264 while(i < 0 || i >=
max)
300 const float clamped_filling =
CLAMPS(filling, 0.0f, 0.9999f);
301 return -logf(1.0f - clamped_filling) /
MAX(crystal_area, FLT_EPSILON);
317 if(!(
mu > 0.0f))
return 0;
321 const float target = expf(-
mu);
322 float product = 1.0f;
325 while(product > target && count < 64)
331 return MAX(count - 1, 0);
335 seed ^ 0x94d049bb133111ebull);
336 return MAX((
int)floorf(deviate + 0.5f), 0);
349static inline float _polygon_area(
const float radius_f,
const float vertices)
351 return 0.5f * vertices * radius_f * radius_f * sinf(2.0f *
M_PI_F / vertices);
357static inline float _polygon_radius(
const float theta,
const float radius_f,
const float vertices,
358 const float rotation)
361 / cosf((2.0f * asinf(cosf(vertices * (theta + rotation))) +
M_PI_F)
362 / (2.0f * vertices));
366#define DT_CRYSTGRAIN_SUBSAMPLES 8
385static inline float _crystal_coverage(
const int dx,
const int dy,
const float radius_f,
const float vertices,
386 const float rotation)
388 const float inradius = radius_f * cosf(
M_PI_F / vertices);
389 const float distance = hypotf((
float)dx, (
float)dy);
390 const float half_diagonal = 0.70710678f;
392 if(distance + half_diagonal <= inradius)
return 1.0f;
393 if(distance - half_diagonal >= radius_f)
return 0.0f;
403 const float sample_radius = hypotf(px, py);
404 if(sample_radius <=
_polygon_radius(atan2f(py, px), radius_f, vertices, rotation)) inside++;
436 const float vertices,
const float rotation)
441 if(!(target_area > 0.0f))
return 1;
443 const int window_radius =
MAX((
int)ceilf(radius_f + 0.5f), 1);
444 const int window_width = 2 * window_radius + 1;
445 float *
const dense = malloc(
sizeof(
float) * (
size_t)window_width * window_width);
448 float raster_area = 0.0f;
449 for(
int y = 0; y < window_width; y++)
451 for(
int x = 0;
x < window_width;
x++)
453 const float alpha =
_crystal_coverage(
x - window_radius, y - window_radius, radius_f, vertices, rotation);
454 dense[(size_t)y * window_width +
x] = alpha;
455 raster_area += alpha;
459 if(raster_area <= FLT_EPSILON)
464 memset(dense, 0,
sizeof(
float) * (
size_t)window_width * window_width);
465 dense[(size_t)window_radius * window_width + window_radius] = target_area;
469 const float gain = target_area / raster_area;
470 for(
size_t k = 0;
k < (size_t)window_width * window_width;
k++) dense[
k] *= gain;
476 for(
int y = 0; y < window_width; y++)
478 for(
int x = 0;
x < window_width;
x++)
480 const float alpha = dense[(size_t)y * window_width +
x];
481 if(alpha <= FLT_EPSILON)
continue;
485 radius =
MAX(radius,
MAX(abs(
x - window_radius), abs(y - window_radius)));
489 if(count <= 0 || area <= FLT_EPSILON)
495 kernel->dx = malloc(
sizeof(
int) * count);
496 kernel->dy = malloc(
sizeof(
int) * count);
497 kernel->alpha = malloc(
sizeof(
float) * count);
510 kernel->radius_f = radius_f;
514 for(
int y = 0; y < window_width; y++)
516 for(
int x = 0;
x < window_width;
x++)
518 const float alpha = dense[(size_t)y * window_width +
x];
519 if(alpha <= FLT_EPSILON)
continue;
522 kernel->dy[
k] = y - window_radius;
549 memset(entry, 0,
sizeof(*entry));
560 const float max_size = 3.0f * mean_size;
562 for(
int attempt = 0; attempt < 8; attempt++)
565 seed + 23u + attempt * 37u),
569 seed + 181u + attempt * 53u);
570 const float random_size =
CLAMPS(expf(log_size), 0.25f,
max_size);
604 return M_PI_F * mean_radius * mean_radius;
624 const int sampled_layers =
MIN(rt->
layers, 4);
627 float total_area = 0.0f;
628 int total_kernels = 0;
630 for(
int layer = 0; layer < sampled_layers; layer++)
639 total_area += bank[
i].footprint.area;
645 return (total_area > FLT_EPSILON && total_kernels > 0)
646 ? total_area / total_kernels
665 const uint64_t kernel_seed = layer_seed ^ ((
uint64_t)(
i + 1) * 0xd1342543de82ef95ull);
668 for(
int k = 0;
k <
i;
k++) _free_crystal_kernel(&bank[
k].footprint);
705 if(!(cap > 0.0f) || !(mass > 0.0f) || !(remaining > 0.0f))
return 0.0f;
707 float light = remaining;
712 const float linear_weight = (light - cap) / cap;
718 return remaining - light * expf(-
weight);
736static inline float _coincident_capture(
const float remaining,
const float cap,
const float alpha,
const int count)
738 if(count <= 0 || !(cap > 0.0f) || !(alpha > 0.0f) || !(remaining > 0.0f))
return 0.0f;
740 float light = remaining;
745 const float exact_steps = (light - cap) / (alpha * cap);
746 const int steps = (exact_steps >= (float)left) ? left : (int)ceilf(exact_steps);
747 light -= (float)steps * alpha * cap;
751 if(left > 0) light *= powf(1.0f - fminf(alpha, 1.0f), (
float)left);
753 return fmaxf(remaining - light, 0.0f);
771 const float remaining_fraction)
773 double capture = 0.0;
778 capture +=
_flat_field_capture(remaining_fraction, area * layer_scale, bank[
i].intensity * area);
803 const float transmitted = 1.0f - remaining_fraction;
804 return (transmitted > FLT_EPSILON) ? 1.0f / transmitted : 1.0f;
807static inline size_t _rgb_index(
const size_t pixel,
const int channel)
809 return 4 * pixel + channel;
827 float *
const remaining,
float *
const exposure)
832 float predicted_remaining = 1.0f;
833 memset(result, 0,
sizeof(
float) * npixels);
834 memcpy(remaining, image,
sizeof(
float) * npixels);
836 for(
int layer = 0; layer < rt->
layers; layer++)
841 predicted_remaining = fmaxf(predicted_remaining
845 for(
int y = 0; y < rt->
height; y++)
850 const size_t index = (size_t)y * rt->
width +
x;
851 if(remaining[index] <= 0.0f)
continue;
855 ^ ((
uint64_t)(uint32_t)world_x << 32)
857 ^ (
uint64_t)(layer + 1) * 0x9e3779b97f4a7c15ull;
862 const int interior = (y >= radius && y < rt->
height - radius &&
x >= radius &&
x < rt->
width - radius);
872 if(crystals <= 0)
continue;
879 const float alpha =
kernel->alpha[0];
880 const float cap = image[index] * alpha * rt->
layer_scale;
882 if(deposited <= 0.0f)
continue;
884 result[index] += deposited;
885 remaining[index] = fmaxf(remaining[index] - deposited, 0.0f);
893 for(
int crystal = 0; crystal < crystals; crystal++)
895 float seed_energy = 0.0f;
896 float original_energy = 0.0f;
898 for(
int tap = 0; tap <
kernel->count; tap++)
901 int yy = y +
kernel->dy[tap];
908 const size_t dst = (size_t)yy *
width + xx;
912 seed_energy += remaining[dst] *
kernel->alpha[tap];
913 original_energy += image[dst] *
kernel->alpha[tap];
915 seed_energy /=
kernel->area;
919 seed_energy = fminf(seed_energy, original_energy);
920 if(seed_energy <= 0.0f)
break;
922 for(
int tap = 0; tap <
kernel->count; tap++)
925 int yy = y +
kernel->dy[tap];
932 const size_t dst = (size_t)yy *
width + xx;
935 const float deposited = seed_energy *
kernel->alpha[tap];
936 result[dst] += deposited;
937 remaining[dst] = fmaxf(remaining[dst] - deposited, 0.0f);
963 float *
const exposure)
968 const int blue_layers = (rt->
layers + 2) / 3;
969 const int green_layers = (rt->
layers + 1) / 3;
970 float predicted_remaining[3] = { 1.0f, 1.0f, 1.0f };
972 0xa24baed4963ee407ull,
973 0x9fb21c651e98df25ull,
974 0xc13fa9a902a6328full
977 memset(
state->result, 0,
sizeof(
float) * npixels * 4);
978 memcpy(
state->remaining,
state->image,
sizeof(
float) * npixels * 4);
980 for(
int layer = 0; layer < rt->
layers; layer++)
983 const int c = (layer < blue_layers) ? 2 : ((layer < blue_layers + green_layers) ? 1 : 0);
984 const int sublayer = (c == 2) ? layer : ((c == 1) ? layer - blue_layers : layer - blue_layers - green_layers);
987 predicted_remaining[c] = fmaxf(predicted_remaining[c]
991 for(
int y = 0; y <
height; y++)
996 const size_t index = (size_t)y *
width +
x;
1000 if(remaining_total <= 0.0f)
continue;
1004 ^ ((
uint64_t)(uint32_t)world_x << 32)
1006 ^ (
uint64_t)(sublayer + 1) * 0x9e3779b97f4a7c15ull;
1007 const uint64_t channel_seed = shared_seed ^ channel_salt[c];
1009 const uint64_t pixel_seed = use_shared ? shared_seed : channel_seed;
1014 const int interior = (y >= radius && y <
height - radius &&
x >= radius &&
x <
width - radius);
1018 if(crystals <= 0)
continue;
1022 const float alpha =
kernel->alpha[0];
1025 if(deposited <= 0.0f)
continue;
1033 for(
int crystal = 0; crystal < crystals; crystal++)
1035 float seed_energy = 0.0f;
1036 float original_energy = 0.0f;
1038 for(
int tap = 0; tap <
kernel->count; tap++)
1041 int yy = y +
kernel->dy[tap];
1048 const size_t dst = (size_t)yy *
width + xx;
1056 seed_energy /=
kernel->area;
1058 const float captured = fminf(seed_energy, original_energy);
1059 if(captured <= 0.0f)
break;
1061 for(
int tap = 0; tap <
kernel->count; tap++)
1064 int yy = y +
kernel->dy[tap];
1071 const size_t dst = (size_t)yy *
width + xx;
1072 const float deposited = captured *
kernel->alpha[tap];
1100 for(
int y = 0; y <
height; y++)
1102 const size_t row = (size_t)y *
width;
1105 const size_t k =
row +
x;
1107 ? dt_ioppr_get_rgb_matrix_luminance(in + 4 *
k, work_profile->
matrix_in, work_profile->
lut_in,
1108 work_profile->unbounded_coeffs_in, work_profile->
lutsize,
1129 for(
int y = 0; y <
height; y++)
1131 const size_t row = (size_t)y *
width;
1134 const size_t k =
row +
x;
1135 const float red = fmaxf(in[4 *
k + 0], 0.0f);
1136 const float green = fmaxf(in[4 *
k + 1], 0.0f);
1137 const float blue = fmaxf(in[4 *
k + 2], 0.0f);
1159 const float *
const restrict image,
const float *
const restrict result,
1160 const int width,
const int height,
const float exposure)
1163 for(
int y = 0; y <
height; y++)
1165 const size_t row = (size_t)y *
width;
1168 const size_t k =
row +
x;
1169 const float grainy = fmaxf(result[
k] * exposure, 0.0f);
1170 const float ratio = (image[
k] > 1e-6f) ? grainy / image[
k] : 0.0f;
1172 out[4 *
k + 0] = fmaxf(in[4 *
k + 0] * ratio, 0.0f);
1173 out[4 *
k + 1] = fmaxf(in[4 *
k + 1] * ratio, 0.0f);
1174 out[4 *
k + 2] = fmaxf(in[4 *
k + 2] * ratio, 0.0f);
1189 const float *
const restrict image,
const float *
const restrict result,
1190 const int width,
const int height,
const float exposure_r,
1191 const float exposure_g,
const float exposure_b,
const float colorfulness)
1194 for(
int y = 0; y <
height; y++)
1196 const size_t row = (size_t)y *
width;
1199 const size_t k =
row +
x;
1203 const float grain_r = (exposure_r > 0.0f) ? fmaxf(result[
_rgb_index(
k, 0)] * exposure_r, 0.0f) : image_r;
1204 const float grain_g = (exposure_g > 0.0f) ? fmaxf(result[
_rgb_index(
k, 1)] * exposure_g, 0.0f) : image_g;
1205 const float grain_b = (exposure_b > 0.0f) ? fmaxf(result[
_rgb_index(
k, 2)] * exposure_b, 0.0f) : image_b;
1206 const float residual_r = grain_r - image_r;
1207 const float residual_g = grain_g - image_g;
1208 const float residual_b = grain_b - image_b;
1209 const float mean = (residual_r + residual_g + residual_b) / 3.0f;
1211 out[4 *
k + 0] = in[4 *
k + 0] + mean + (residual_r - mean) * colorfulness;
1212 out[4 *
k + 1] = in[4 *
k + 1] + mean + (residual_g - mean) * colorfulness;
1213 out[4 *
k + 2] = in[4 *
k + 2] + mean + (residual_b - mean) * colorfulness;
1219#define DT_CRYSTGRAIN_CL_PROGRAM 36
1220#define DT_CRYSTGRAIN_REDUCESIZE 64
1238 cl_mem *
const dev_params, cl_mem *
const dev_alpha)
1241 size_t total_taps = 0;
1249 const size_t stride = (size_t)(2 * radius + 1);
1252 params[
i][2] = (float)radius;
1253 params[
i][3] = (float)total_taps;
1254 total_taps += stride * stride;
1257 float *
const alpha = calloc(total_taps,
sizeof(
float));
1258 if(
IS_NULL_PTR(alpha))
return CL_MEM_OBJECT_ALLOCATION_FAILURE;
1263 const int radius = footprint->
radius;
1264 const size_t stride = (size_t)(2 * radius + 1);
1265 const size_t offset = (size_t)params[
i][3];
1267 for(
int tap = 0; tap < footprint->
count; tap++)
1268 alpha[offset + (
size_t)(footprint->
dy[tap] + radius) * stride + (
size_t)(footprint->
dx[tap] + radius)]
1269 = footprint->
alpha[tap];
1274 cl_int err = (
IS_NULL_PTR(*dev_params) ||
IS_NULL_PTR(*dev_alpha)) ? CL_MEM_OBJECT_ALLOCATION_FAILURE : CL_SUCCESS;
1276 if(err == CL_SUCCESS)
1281 if(err != CL_SUCCESS)
1303 cl_mem dev_remaining,
float *
const exposure)
1305 cl_int err = CL_SUCCESS;
1309 const size_t buffer_size =
sizeof(float) * (
size_t)
width *
height;
1310 float predicted_remaining = 1.0f;
1316 if(err != CL_SUCCESS)
return err;
1319 if(err != CL_SUCCESS)
return err;
1321 for(
int layer = 0; layer < rt->
layers; layer++)
1324 cl_mem dev_kernel_bank = NULL;
1325 cl_mem dev_kernel_alpha = NULL;
1327 const int roi_x = rt->
roi_x;
1328 const int roi_y = rt->
roi_y;
1330 const cl_ulong base_seed = (cl_ulong)rt->
base_seed;
1334 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
1337 predicted_remaining = fmaxf(predicted_remaining
1343 if(err != CL_SUCCESS)
return err;
1361 if(err != CL_SUCCESS)
return err;
1395 module->data = NULL;
1399 cl_mem dev_in, cl_mem dev_out)
1406 const int devid = pipe->
devid;
1416 cl_int err = CL_SUCCESS;
1417 float exposure[3] = { 1.0f, 1.0f, 1.0f };
1419 if(
width <= 0 || height <= 0 || d->layers <= 0 || d->filling <= 0.0f)
1421 size_t origin[] = { 0, 0, 0 };
1422 size_t region[] = { (size_t)
width, (
size_t)
height, 1 };
1426 cl_mem dev_image = NULL;
1427 cl_mem dev_result = NULL;
1428 cl_mem dev_remaining = NULL;
1429 cl_mem dev_image_rgb = NULL;
1430 cl_mem dev_result_rgb = NULL;
1431 cl_mem dev_remaining_rgb = NULL;
1433 cl_float *profile_lut_cl = NULL;
1434 cl_mem dev_profile_info = NULL;
1435 cl_mem dev_profile_lut = NULL;
1442 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
1449 .roi_x = roi_out->
x,
1450 .roi_y = roi_out->
y,
1451 .layers =
d->layers,
1452 .layer_scale = 0.0f,
1453 .filling =
d->filling,
1454 .grain_size =
d->grain_size,
1455 .size_stddev =
d->size_stddev,
1456 .kernel_scale = kernel_scale,
1457 .inv_scale = 1.0f / kernel_scale,
1458 .channel_correlation =
d->channel_correlation,
1465 ^ 0x9e3779b97f4a7c15ull
1474 rt.
layer_scale =
d->layer_capture /
MAX((
float)
d->layers, 1.0f) /
MAX(current_surface, FLT_EPSILON);
1475 const int blue_layers = (rt.
layers + 2) / 3;
1476 const int green_layers = (rt.
layers + 1) / 3;
1483 &dev_profile_info, &dev_profile_lut);
1484 if(err != CL_SUCCESS)
goto error;
1486 const int use_work_profile = (!
IS_NULL_PTR(work_profile)) ? 1 : 0;
1495 if(err != CL_SUCCESS)
goto error;
1497 err =
_simulate_channel_cl(devid, gd, &rt, dev_image, dev_result, dev_remaining, &exposure[0]);
1498 if(err != CL_SUCCESS)
goto error;
1516 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
1525 if(err != CL_SUCCESS)
goto error;
1527 const size_t color_buffer_size =
sizeof(float) * (
size_t)
width *
height * 4;
1532 if(err != CL_SUCCESS)
goto error;
1535 if(err != CL_SUCCESS)
goto error;
1537 float predicted_remaining[3] = { 1.0f, 1.0f, 1.0f };
1547 const cl_ulong base_seed = (cl_ulong)rt.
base_seed;
1553 for(
int layer = 0; layer < rt.
layers; layer++)
1556 cl_mem dev_kernel_bank = NULL;
1557 cl_mem dev_kernel_alpha = NULL;
1558 const int active_channel = (layer < blue_layers) ? 2 : ((layer < blue_layers + green_layers) ? 1 : 0);
1559 const int sublayer = (active_channel == 2)
1561 : ((active_channel == 1) ? layer - blue_layers : layer - blue_layers - green_layers);
1564 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
1567 predicted_remaining[active_channel]
1568 = fmaxf(predicted_remaining[active_channel]
1574 if(err != CL_SUCCESS)
goto error;
1583 if(err != CL_SUCCESS)
goto error;
1608 return (err == CL_SUCCESS) ?
TRUE :
FALSE;
1619 d->filling =
p->filling * 0.01f;
1620 d->grain_size =
p->grain_size;
1621 d->layers =
p->layers;
1622 d->size_stddev =
p->size_stddev;
1623 d->layer_capture = exp2f(
p->layer_capture);
1624 d->channel_correlation =
p->channel_correlation * 0.01f;
1625 d->colorspace_saturation =
p->colorspace_saturation * 0.01f;
1641 const void *
const ivoid,
void *
const ovoid)
1647 const float *
const restrict in = (
const float *
const)ivoid;
1648 float *
const restrict
out = (
float *
const)
ovoid;
1655 if(
width <= 0 || height <= 0 || d->layers <= 0 || d->filling <= 0.0f)
1661 float *image = NULL;
1662 float *result = NULL;
1663 float *remaining = NULL;
1664 float *image_rgb = NULL;
1665 float *result_rgb = NULL;
1666 float *remaining_rgb = NULL;
1685 .roi_x = roi_out->
x,
1686 .roi_y = roi_out->
y,
1687 .layers =
d->layers,
1688 .layer_scale = 0.0f,
1689 .filling =
d->filling,
1690 .grain_size =
d->grain_size,
1691 .size_stddev =
d->size_stddev,
1692 .kernel_scale = kernel_scale,
1693 .inv_scale = 1.0f / kernel_scale,
1694 .channel_correlation =
d->channel_correlation,
1701 ^ 0x9e3779b97f4a7c15ull
1710 rt.
layer_scale =
d->layer_capture /
MAX((
float)
d->layers, 1.0f) /
MAX(current_surface, FLT_EPSILON);
1715 float mono_exposure = 1.0f;
1735 float color_exposure[3] = { 1.0f, 1.0f, 1.0f };
1738 .result = result_rgb,
1739 .remaining = remaining_rgb
1755 color_exposure[0], color_exposure[1], color_exposure[2],
1756 d->colorspace_saturation);
1774 gtk_widget_set_visible(
g->channel_correlation, is_color);
1775 gtk_widget_set_visible(
g->colorspace_saturation, is_color);
1788 gtk_widget_set_tooltip_text(
g->mode, _(
"simulate one shared B&W grain field or one shared blue/green/red-sensitive color grain stack"));
1789 g_signal_connect(G_OBJECT(
g->mode),
"value-changed", G_CALLBACK(
_mode_changed), self);
1793 gtk_widget_set_tooltip_text(
g->filling, _(
"surface ratio occupied by silver-halide crystals in each layer"));
1798 gtk_widget_set_tooltip_text(
g->grain_size, _(
"average crystal diameter, in full-resolution pixels. The same crystals are simulated at every preview and export resolution, so the rendered grain stays consistent across sizes"));
1802 gtk_widget_set_tooltip_text(
g->layers, _(
"number of crystal layers stacked through the emulsion"));
1807 gtk_widget_set_tooltip_text(
g->layer_capture, _(
"0 EV means one layer captures its neutral 1/layers share after normalization by the rasterized grain surface; positive values increase that capture and negative values decrease it"));
1811 gtk_widget_set_tooltip_text(
g->channel_correlation, _(
"probability that blue-, green- and red-sensitive sub-layers reuse the same crystal births and shapes at matching depths"));
1815 gtk_widget_set_tooltip_text(
g->colorspace_saturation, _(
"scale only the chromatic amplitude of the RGB grain residual while keeping its achromatic strength unchanged"));
1818 gtk_widget_set_tooltip_text(
g->size_stddev, _(
"log-normal standard deviation of crystal sizes"));
static void error(char *msg)
void dt_bauhaus_slider_set_soft_range(GtkWidget *widget, float soft_min, float soft_max)
void dt_bauhaus_slider_set_digits(GtkWidget *widget, int val)
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
static float dt_camera_rgb_luminance(const float4 rgb)
void dt_ioppr_free_iccprofile_params_cl(dt_colorspaces_iccprofile_info_cl_t **_profile_info_cl, cl_float **_profile_lut_cl, cl_mem *_dev_profile_info, cl_mem *_dev_profile_lut)
free parameters build with the previous function.
cl_int dt_ioppr_build_iccprofile_params_cl(const dt_iop_order_iccprofile_info_t *const profile_info, const int devid, dt_colorspaces_iccprofile_info_cl_t **_profile_info_cl, cl_float **_profile_lut_cl, cl_mem *_dev_profile_info, cl_mem *_dev_profile_lut)
build the required parameters for a kernel that uses a profile info.
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
const dt_colormatrix_t dt_aligned_pixel_t out
static float envelope(const float xx)
static float _polygon_radius(const float theta, const float radius_f, const float vertices, const float rotation)
Polar radius of one crystal boundary at angle theta.
static __DT_CLONE_TARGETS__ int _create_crystal_kernel(dt_iop_crystgrain_kernel_t *const kernel, const float radius_f, const float vertices, const float rotation)
Build one partially-occluding crystal footprint for a layer.
const char ** description(struct dt_iop_module_t *self)
static __DT_CLONE_TARGETS__ float _predict_layer_capture(const dt_iop_crystgrain_layer_kernel_t *const bank, const float layer_scale, const float remaining_fraction)
Predict the mean captured energy of one flat-field layer.
#define DT_CRYSTGRAIN_CL_PROGRAM
#define DT_CRYSTGRAIN_LAYER_KERNELS
static float _uniform_random(const uint64_t seed)
Turn a 64-bit seed into a uniform random number in [0; 1).
static __DT_CLONE_TARGETS__ void _apply_mono_grain_kernel(const float *const restrict in, float *const restrict out, const float *const restrict image, const float *const restrict result, const int width, const int height, const float exposure)
Apply one monochrome grain field back onto the RGB image.
static int _reflect_index(int i, const int max)
Mirror indices outside the current buffer like scipy ‘boundary='symm’`.
static float _average_grain_surface(const dt_iop_crystgrain_runtime_t *const rt)
Estimate the reference grain surface used to normalize layer capture.
static size_t _rgb_index(const size_t pixel, const int channel)
static __DT_CLONE_TARGETS__ unsigned int _hash_string(const char *s)
Hash a string into a stable 32-bit seed.
static void _free_layer_kernel_bank(dt_iop_crystgrain_layer_kernel_t *const bank)
Release all crystal footprints from one layer bank.
static __DT_CLONE_TARGETS__ int _pick_layer_kernel(dt_iop_crystgrain_layer_kernel_t *const entry, const dt_iop_crystgrain_runtime_t *const rt, const uint64_t seed)
Pick one crystal geometry for one bank entry.
static __DT_CLONE_TARGETS__ void _extract_luminance_kernel(const float *const restrict in, float *const restrict image, const int width, const int height, const dt_iop_order_iccprofile_info_t *const work_profile)
Extract a luminance image from the RGB input buffer.
static void _mode_changed(GtkWidget *widget, dt_iop_module_t *self)
static __DT_CLONE_TARGETS__ void _extract_rgb_kernels(const float *const restrict in, float *const restrict image, const int width, const int height)
Extract the three RGB light channels as scalar images.
#define DT_CRYSTGRAIN_MIN_RADIUS
static float _seed_intensity(const float filling, const float crystal_area)
Map the requested filling ratio to the germ intensity used to plant seeds, in crystals per grid pixel...
static cl_int _upload_layer_bank(const int devid, const dt_iop_crystgrain_layer_kernel_t *const bank, cl_mem *const dev_params, cl_mem *const dev_alpha)
Upload one layer bank to the device.
static __DT_CLONE_TARGETS__ int _simulate_channel(const dt_iop_crystgrain_runtime_t *const rt, const float *const image, float *const result, float *const remaining, float *const exposure)
Simulate one monochrome grain field from one scalar image.
void gui_update(struct dt_iop_module_t *self)
static int _build_layer_kernel_bank(dt_iop_crystgrain_layer_kernel_t *const bank, const dt_iop_crystgrain_runtime_t *const rt, const uint64_t layer_seed)
Build the crystal bank for one layer.
static float _coincident_capture(const float remaining, const float cap, const float alpha, const int count)
Deplete one pixel with count coincident crystals.
void gui_init(struct dt_iop_module_t *self)
void commit_params(dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static __DT_CLONE_TARGETS__ void _finalize_color_grain_kernel(const float *const restrict in, float *const restrict out, const float *const restrict image, const float *const restrict result, const int width, const int height, const float exposure_r, const float exposure_g, const float exposure_b, const float colorfulness)
Finalize the three color grain channels in one pass.
static int _poisson_random(const uint64_t seed, const float mu)
Draw one Poisson deviate of mean mu.
void cleanup_global(dt_iop_module_so_t *module)
void cleanup_pipe(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
static float _crystal_coverage(const int dx, const int dy, const float radius_f, const float vertices, const float rotation)
Estimate the surface of one pixel covered by one crystal.
static float _predict_stack_exposure(const float remaining_fraction)
Predict the exposure compensation of one monochrome grain stack.
static int _simulate_channel_cl(const int devid, dt_iop_crystgrain_global_data_t *const gd, const dt_iop_crystgrain_runtime_t *const rt, cl_mem dev_image, cl_mem dev_result, cl_mem dev_remaining, float *const exposure)
Simulate one grain field entirely on the OpenCL device.
void init_presets(dt_iop_module_so_t *self)
static float _gaussian_random(const uint64_t seed_a, const uint64_t seed_b)
Turn 2 seeds into one gaussian deviate.
static float _polygon_area(const float radius_f, const float vertices)
Analytic surface of one crystal, in current grid pixels squared.
int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid)
void init_pipe(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static __DT_CLONE_TARGETS__ float _average_discrete_grain_surface(const dt_iop_crystgrain_runtime_t *const rt)
Estimate the actual rasterized grain surface at the current scale.
static __DT_CLONE_TARGETS__ int _simulate_color(const dt_iop_crystgrain_runtime_t *const rt, const dt_iop_crystgrain_color_state_t *const state, float *const exposure)
Simulate one color grain stack with shared crystal geometry.
void init_global(dt_iop_module_so_t *module)
int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
static float _flat_field_capture(const float remaining, const float cap, const float mass)
Deplete a flat light field with a continuous germ mass.
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params, const int new_version)
#define DT_CRYSTGRAIN_SUBSAMPLES
static unsigned int splitmix32(const unsigned long seed)
dt_iop_order_iccprofile_info_t * dt_ioppr_get_pipe_work_profile_info(const struct dt_dev_pixelpipe_t *pipe)
static void weight(const float *c1, const float *c2, const float sharpen, dt_aligned_pixel_t weight)
void dt_gui_presets_add_generic(const char *name, dt_dev_operation_t op, const int32_t version, const void *params, const int32_t params_size, const int32_t enabled)
int dt_iop_alloc_image_buffers(struct dt_iop_module_t *const module, const struct dt_iop_roi_t *const roi_in, const struct dt_iop_roi_t *const roi_out,...)
void dt_iop_copy_image_roi(float *const __restrict__ out, const float *const __restrict__ in, const size_t ch, const dt_iop_roi_t *const __restrict__ roi_in, const dt_iop_roi_t *const __restrict__ roi_out, const int zero_pad)
#define DT_IMGSZ_CLEARBUF
static void dt_iop_image_copy_by_size(float *const __restrict__ out, const float *const __restrict__ in, const size_t width, const size_t height, const size_t ch)
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
float dt_dev_get_module_scale(const dt_dev_pixelpipe_t *const pipe, const dt_iop_roi_t *const roi_in)
@ IOP_FLAGS_INCLUDE_IN_STYLES
@ IOP_FLAGS_SUPPORTS_BLENDING
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
GtkWidget * dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *param)
static dt_iop_gui_data_t * dt_iop_gui_data(const struct dt_iop_module_t *m)
The module's GUI data blob, NULL-safe for headless callers: IOP process() implementations read it for...
#define IOP_GUI_ALLOC(module)
static float kernel(const float *x, const float *y)
float *const restrict luminance
float *const restrict const size_t k
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
static const int max_size
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
void * dt_opencl_alloc_device_buffer(const int devid, const size_t size)
int dt_opencl_create_kernel(const int prog, const char *name)
void * dt_opencl_copy_host_to_device_constant(const int devid, const size_t size, void *host)
int dt_opencl_write_buffer_to_device(const int devid, void *host, void *device, const size_t offset, const size_t size, const int blocking)
int dt_opencl_enqueue_copy_image(const int devid, cl_mem src, cl_mem dst, size_t *orig_src, size_t *orig_dst, size_t *region)
void dt_opencl_free_kernel(const int kernel)
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
int dt_opencl_enqueue_copy_buffer_to_buffer(const int devid, cl_mem src_buffer, cl_mem dst_buffer, size_t srcoffset, size_t dstoffset, size_t size)
void dt_opencl_release_mem_object(cl_mem mem)
#define __OMP_PARALLEL_FOR__(...)
#define dt_pixelpipe_cache_free_align(mem)
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...
const float uint32_t state[4]
unsigned __int64 uint64_t
The device-side view of a dt_iop_order_iccprofile_info_t: the scalar fields only.
struct dt_iop_module_t *void * data
struct dt_develop_t * dev
char filename[DT_MAX_FILENAME_LEN]
float channel_correlation
dt_iop_crystgrain_mode_t mode
float colorspace_saturation
int kernel_simulate_layer_color
int kernel_finalize_color
int kernel_simulate_layer
int kernel_extract_luminance
GtkWidget * colorspace_saturation
GtkWidget * layer_capture
GtkWidget * channel_correlation
dt_iop_crystgrain_kernel_t footprint
float colorspace_saturation
dt_iop_crystgrain_mode_t mode
float channel_correlation
float channel_correlation
dt_iop_global_data_t * data
dt_iop_global_data_t * global_data
A profile reduced to the arithmetic the pixel loop can run: two matrices and six tone-curve LUTs,...
int nonlinearlut
Non-zero when the profile has tone curves at all; tested as a boolean everywhere, but it is really th...
int lutsize
Entry count of each of the six LUTs. Always 65536 in practice: both callers of dt_ioppr_init_profile_...
float * lut_in[3]
Per-channel encoded -> linear tone curve, lutsize entries each, sampled over [0,1]....
dt_colormatrix_t matrix_in
RGB -> XYZ (D50), row-major. matrix_in[1][*] is the luminance row. NaN in [0][0] marks the whole prof...
Region of interest passed through the pixelpipe.
#define __DT_CLONE_TARGETS__