Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
crystgrain.c
Go to the documentation of this file.
1/*
2 This file is part of the Ansel project.
3 Copyright (C) 2026 Aurélien PIERRE.
4
5 Ansel is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 Ansel is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with Ansel. If not, see <http://www.gnu.org/licenses/>.
17*/
18#ifdef HAVE_CONFIG_H
19#include "config.h"
21#endif
22
23#include "widgets/bauhaus.h"
24#include "common/imagebuf.h"
25#include "common/opencl.h"
26#include "develop/iop_profile.h"
27#include "math/math.h"
28#include "system/macros.h"
29#include "system/openmp.h"
31#include "system/mem_alloc.h"
33#include "develop/imageop.h"
34#include "develop/imageop_gui.h"
35#include "iop/noise_generator.h"
36#include "gui/presets.h"
37#include "iop/iop_api.h"
38
39#include <float.h>
40#include <gtk/gtk.h>
41#include <math.h>
42#include <stdint.h>
43#include <stdlib.h>
44#include <string.h>
45
47
48#define DT_CRYSTGRAIN_LAYER_KERNELS 16
49
50// Smallest crystal radius, in current grid pixels, the rasterizer is asked to
51// resolve. Below that a crystal covers less than a millionth of a pixel and its
52// germ intensity would explode without any visible return.
53#define DT_CRYSTGRAIN_MIN_RADIUS 1e-3f
54
56{
57 DT_CRYSTGRAIN_MONO = 0, // $DESCRIPTION: "B&W"
58 DT_CRYSTGRAIN_COLOR = 1 // $DESCRIPTION: "color"
60
62{
63 dt_iop_crystgrain_mode_t mode; // $DEFAULT: DT_CRYSTGRAIN_MONO $DESCRIPTION: "mode"
64 float filling; // $MIN: 0.0 $MAX: 95.0 $DEFAULT: 25.0 $DESCRIPTION: "Average layer filling"
65 float grain_size; // $MIN: 1.0 $MAX: 31.0 $DEFAULT: 4.0 $DESCRIPTION: "Crystals average size"
66 int layers; // $MIN: 1 $MAX: 64 $DEFAULT: 30 $DESCRIPTION: "Crystals layers"
67 float size_stddev; // $MIN: 0.0 $MAX: 2.0 $DEFAULT: 0.25 $DESCRIPTION: "Crystals size variability"
68 float layer_capture; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "Layer sensitivity"
69 float channel_correlation; // $MIN: 0.0 $MAX: 100.0 $DEFAULT: 67.0 $DESCRIPTION: "Inter-channel grain correlation"
70 float colorspace_saturation; // $MIN: 0.0 $MAX: 100.0 $DEFAULT: 67.0 $DESCRIPTION: "Grain colorfulness"
72
84
96
107
109{
111 float intensity; // crystals per grid pixel, see _seed_intensity()
112 float vertices;
113 float rotation;
115
132
139
140#ifdef HAVE_OPENCL
152#endif
153
154
155const char *name()
156{
157 return _("Photographic grain");
158}
159
160const char **description(struct dt_iop_module_t *self)
161{
162 return dt_iop_set_description(self, _("simulate photographic grain from stacked silver-halide crystal layers"),
163 _("creative"),
164 _("non-linear, RGB, scene-referred"),
165 _("non-linear, RGB"),
166 _("non-linear, RGB, scene-referred"));
167}
168
173
175{
176 return IOP_GROUP_EFFECTS;
177}
178
180{
181 return IOP_CS_RGB;
182}
183
185{
187
189 p.filling = 85.0f;
190 p.grain_size = 4.0f;
191 p.layers = 30;
192 p.size_stddev = 0.25f;
193 p.layer_capture = 0.0f;
194 p.channel_correlation = 67.0f;
195 p.colorspace_saturation = 67.0f;
196 dt_gui_presets_add_generic(_("color grain"), self->op, self->version(), &p, sizeof(p), 1);
197
198 p.mode = DT_CRYSTGRAIN_MONO;
199 p.filling = 25.0f;
200 p.grain_size = 4.0f;
201 p.layers = 30;
202 p.size_stddev = 0.25f;
203 p.layer_capture = 0.0f;
204 p.channel_correlation = 67.0f;
205 p.colorspace_saturation = 67.0f;
206 dt_gui_presets_add_generic(_("B&W grain"), self->op, self->version(), &p, sizeof(p), 1);
207}
208
209int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params,
210 const int new_version)
211{
212 if((old_version == 1 || old_version == 8) && new_version == 9)
213 {
214 const dt_iop_crystgrain_params_t *o = old_params;
215 dt_iop_crystgrain_params_t *n = new_params;
216 *n = *o;
217 return 0;
218 }
219
220 return 1;
221}
222
227static unsigned int _hash_string(const char *s)
228{
229 unsigned int h = 0;
230 while(*s) h = 33 * h ^ (unsigned int)*s++;
231 return h;
232}
233
237static inline float _uniform_random(const uint64_t seed)
238{
239 return splitmix32(seed) * 0x1.0p-32f;
240}
241
249static inline float _gaussian_random(const uint64_t seed_a, const uint64_t seed_b)
250{
251 const float u1 = fmaxf(_uniform_random(seed_a), FLT_MIN);
252 const float u2 = _uniform_random(seed_b);
253 return sqrtf(-2.0f * logf(u1)) * cosf(2.0f * M_PI_F * u2);
254}
255
260static inline int _reflect_index(int i, const int max)
261{
262 if(max <= 1) return 0;
263
264 while(i < 0 || i >= max)
265 {
266 if(i < 0)
267 i = -i - 1;
268 else
269 i = 2 * max - i - 1;
270 }
271
272 return i;
273}
274
298static inline float _seed_intensity(const float filling, const float crystal_area)
299{
300 const float clamped_filling = CLAMPS(filling, 0.0f, 0.9999f);
301 return -logf(1.0f - clamped_filling) / MAX(crystal_area, FLT_EPSILON);
302}
303
315static inline int _poisson_random(const uint64_t seed, const float mu)
316{
317 if(!(mu > 0.0f)) return 0;
318
319 if(mu < 12.0f)
320 {
321 const float target = expf(-mu);
322 float product = 1.0f;
323 int count = 0;
324
325 while(product > target && count < 64)
326 {
327 count++;
328 product *= _uniform_random(seed + (uint64_t)count * 0x9e3779b97f4a7c15ull);
329 }
330
331 return MAX(count - 1, 0);
332 }
333
334 const float deviate = mu + sqrtf(mu) * _gaussian_random(seed ^ 0xbf58476d1ce4e5b9ull,
335 seed ^ 0x94d049bb133111ebull);
336 return MAX((int)floorf(deviate + 0.5f), 0);
337}
338
349static inline float _polygon_area(const float radius_f, const float vertices)
350{
351 return 0.5f * vertices * radius_f * radius_f * sinf(2.0f * M_PI_F / vertices);
352}
353
357static inline float _polygon_radius(const float theta, const float radius_f, const float vertices,
358 const float rotation)
359{
360 const float envelope = cosf(M_PI_F / vertices)
361 / cosf((2.0f * asinf(cosf(vertices * (theta + rotation))) + M_PI_F)
362 / (2.0f * vertices));
363 return radius_f * envelope;
364}
365
366#define DT_CRYSTGRAIN_SUBSAMPLES 8
367
385static inline float _crystal_coverage(const int dx, const int dy, const float radius_f, const float vertices,
386 const float rotation)
387{
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; // half-diagonal of the unit pixel square
391
392 if(distance + half_diagonal <= inradius) return 1.0f;
393 if(distance - half_diagonal >= radius_f) return 0.0f;
394
395 int inside = 0;
396
397 for(int sy = 0; sy < DT_CRYSTGRAIN_SUBSAMPLES; sy++)
398 {
399 const float py = (float)dy - 0.5f + ((float)sy + 0.5f) / (float)DT_CRYSTGRAIN_SUBSAMPLES;
400 for(int sx = 0; sx < DT_CRYSTGRAIN_SUBSAMPLES; sx++)
401 {
402 const float px = (float)dx - 0.5f + ((float)sx + 0.5f) / (float)DT_CRYSTGRAIN_SUBSAMPLES;
403 const float sample_radius = hypotf(px, py);
404 if(sample_radius <= _polygon_radius(atan2f(py, px), radius_f, vertices, rotation)) inside++;
405 }
406 }
407
408 return (float)inside / (float)(DT_CRYSTGRAIN_SUBSAMPLES * DT_CRYSTGRAIN_SUBSAMPLES);
409}
410
435static int _create_crystal_kernel(dt_iop_crystgrain_kernel_t *const kernel, const float radius_f,
436 const float vertices, const float rotation)
437{
438 memset(kernel, 0, sizeof(*kernel));
439
440 const float target_area = _polygon_area(radius_f, vertices);
441 if(!(target_area > 0.0f)) return 1;
442
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);
446 if(IS_NULL_PTR(dense)) return 1;
447
448 float raster_area = 0.0f;
449 for(int y = 0; y < window_width; y++)
450 {
451 for(int x = 0; x < window_width; x++)
452 {
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;
456 }
457 }
458
459 if(raster_area <= FLT_EPSILON)
460 {
461 // The crystal is smaller than one subsample: keep it as one tap carrying
462 // its exact analytic surface rather than dropping it, so the germ
463 // statistics stay valid all the way down.
464 memset(dense, 0, sizeof(float) * (size_t)window_width * window_width);
465 dense[(size_t)window_radius * window_width + window_radius] = target_area;
466 }
467 else
468 {
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;
471 }
472
473 int count = 0;
474 int radius = 0;
475 float area = 0.0f;
476 for(int y = 0; y < window_width; y++)
477 {
478 for(int x = 0; x < window_width; x++)
479 {
480 const float alpha = dense[(size_t)y * window_width + x];
481 if(alpha <= FLT_EPSILON) continue;
482
483 count++;
484 area += alpha;
485 radius = MAX(radius, MAX(abs(x - window_radius), abs(y - window_radius)));
486 }
487 }
488
489 if(count <= 0 || area <= FLT_EPSILON)
490 {
491 free(dense);
492 return 1;
493 }
494
495 kernel->dx = malloc(sizeof(int) * count);
496 kernel->dy = malloc(sizeof(int) * count);
497 kernel->alpha = malloc(sizeof(float) * count);
498 if(IS_NULL_PTR(kernel->dx) || IS_NULL_PTR(kernel->dy) || IS_NULL_PTR(kernel->alpha))
499 {
500 free(kernel->dx);
501 free(kernel->dy);
502 free(kernel->alpha);
503 memset(kernel, 0, sizeof(*kernel));
504 free(dense);
505 return 1;
506 }
507
508 kernel->count = count;
509 kernel->radius = radius;
510 kernel->radius_f = radius_f;
511 kernel->area = area;
512
513 int k = 0;
514 for(int y = 0; y < window_width; y++)
515 {
516 for(int x = 0; x < window_width; x++)
517 {
518 const float alpha = dense[(size_t)y * window_width + x];
519 if(alpha <= FLT_EPSILON) continue;
520
521 kernel->dx[k] = x - window_radius;
522 kernel->dy[k] = y - window_radius;
523 kernel->alpha[k] = alpha;
524 k++;
525 }
526 }
527
528 free(dense);
529 return 0;
530}
534static inline __attribute__((always_inline)) void _free_crystal_kernel(dt_iop_crystgrain_kernel_t *const kernel)
535{
536 free(kernel->dx);
537 free(kernel->dy);
538 free(kernel->alpha);
539 memset(kernel, 0, sizeof(*kernel));
540}
541
547 const dt_iop_crystgrain_runtime_t *const rt, const uint64_t seed)
548{
549 memset(entry, 0, sizeof(*entry));
550
551 // The size distribution is drawn in full-resolution pixels — the unit the
552 // slider is authored in — and only converted to the current grid at the very
553 // end, by one linear factor. Drawing it directly in grid pixels instead is
554 // what used to make the crystal population itself resolution-dependent: the
555 // old form subtracted one whole pixel from the size before halving it, and
556 // an affine map cannot commute with a scale change, so halving the preview
557 // scale shrank the crystals by more than half and eventually pinned them at
558 // the 1-pixel floor.
559 const float mean_size = MAX(rt->grain_size, 1.0f);
560 const float max_size = 3.0f * mean_size;
561
562 for(int attempt = 0; attempt < 8; attempt++)
563 {
564 const float vertices = CLAMPS(6.0f + 1.5f * _gaussian_random(seed + 17u + attempt * 31u,
565 seed + 23u + attempt * 37u),
566 3.0f, 10.0f);
567 const float rotation = 2.0f * M_PI_F * _uniform_random(seed + 101u + attempt * 43u);
568 const float log_size = logf(mean_size) + rt->size_stddev * _gaussian_random(seed + 151u + attempt * 47u,
569 seed + 181u + attempt * 53u);
570 const float random_size = CLAMPS(expf(log_size), 0.25f, max_size);
571 const float radius_f = MAX(0.5f * random_size * rt->kernel_scale, DT_CRYSTGRAIN_MIN_RADIUS);
572
573 if(_create_crystal_kernel(&entry->footprint, radius_f, vertices, rotation) == 0)
574 {
575 entry->intensity = _seed_intensity(rt->filling, entry->footprint.area);
576 entry->vertices = vertices;
577 entry->rotation = rotation;
578 return 0;
579 }
580 }
581
582 if(_create_crystal_kernel(&entry->footprint, MAX(0.5f * mean_size * rt->kernel_scale, DT_CRYSTGRAIN_MIN_RADIUS),
583 4.0f, 0.0f) != 0)
584 return 1;
585
586 entry->intensity = _seed_intensity(rt->filling, entry->footprint.area);
587 entry->vertices = 4.0f;
588 entry->rotation = 0.0f;
589 return 0;
590}
591
601static inline float _average_grain_surface(const dt_iop_crystgrain_runtime_t *const rt)
602{
603 const float mean_radius = MAX(0.5f * MAX(rt->grain_size, 1.0f) * rt->kernel_scale, DT_CRYSTGRAIN_MIN_RADIUS);
604 return M_PI_F * mean_radius * mean_radius;
605}
606
608 const dt_iop_crystgrain_runtime_t *const rt, const uint64_t layer_seed);
610
623{
624 const int sampled_layers = MIN(rt->layers, 4);
625 if(sampled_layers <= 0) return _average_grain_surface(rt);
626
627 float total_area = 0.0f;
628 int total_kernels = 0;
629
630 for(int layer = 0; layer < sampled_layers; layer++)
631 {
633 const uint64_t layer_seed = rt->base_seed + layer * 4099u;
634
635 if(_build_layer_kernel_bank(bank, rt, layer_seed) != 0)
636 return _average_grain_surface(rt);
637
638 for(int i = 0; i < DT_CRYSTGRAIN_LAYER_KERNELS; i++)
639 total_area += bank[i].footprint.area;
640
641 total_kernels += DT_CRYSTGRAIN_LAYER_KERNELS;
643 }
644
645 return (total_area > FLT_EPSILON && total_kernels > 0)
646 ? total_area / total_kernels
648}
649
659 const dt_iop_crystgrain_runtime_t *const rt, const uint64_t layer_seed)
660{
662
663 for(int i = 0; i < DT_CRYSTGRAIN_LAYER_KERNELS; i++)
664 {
665 const uint64_t kernel_seed = layer_seed ^ ((uint64_t)(i + 1) * 0xd1342543de82ef95ull);
666 if(_pick_layer_kernel(&bank[i], rt, kernel_seed) != 0)
667 {
668 for(int k = 0; k < i; k++) _free_crystal_kernel(&bank[k].footprint);
669 return 1;
670 }
671 }
672
673 return 0;
674}
675
681{
682 for(int i = 0; i < DT_CRYSTGRAIN_LAYER_KERNELS; i++) _free_crystal_kernel(&bank[i].footprint);
683}
684
703static inline float _flat_field_capture(const float remaining, const float cap, const float mass)
704{
705 if(!(cap > 0.0f) || !(mass > 0.0f) || !(remaining > 0.0f)) return 0.0f;
706
707 float light = remaining;
708 float weight = mass;
709
710 if(light > cap)
711 {
712 const float linear_weight = (light - cap) / cap;
713 if(weight <= linear_weight) return cap * weight;
714 light = cap;
715 weight -= linear_weight;
716 }
717
718 return remaining - light * expf(-weight);
719}
720
736static inline float _coincident_capture(const float remaining, const float cap, const float alpha, const int count)
737{
738 if(count <= 0 || !(cap > 0.0f) || !(alpha > 0.0f) || !(remaining > 0.0f)) return 0.0f;
739
740 float light = remaining;
741 int left = count;
742
743 if(light > cap)
744 {
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;
748 left -= steps;
749 }
750
751 if(left > 0) light *= powf(1.0f - fminf(alpha, 1.0f), (float)left);
752
753 return fmaxf(remaining - light, 0.0f);
754}
755
770static float _predict_layer_capture(const dt_iop_crystgrain_layer_kernel_t *const bank, const float layer_scale,
771 const float remaining_fraction)
772{
773 double capture = 0.0;
774
775 for(int i = 0; i < DT_CRYSTGRAIN_LAYER_KERNELS; i++)
776 {
777 const float area = bank[i].footprint.area;
778 capture += _flat_field_capture(remaining_fraction, area * layer_scale, bank[i].intensity * area);
779 }
780
781 return MAX((float)(capture / DT_CRYSTGRAIN_LAYER_KERNELS), 0.0f);
782}
783
801static inline float _predict_stack_exposure(const float remaining_fraction)
802{
803 const float transmitted = 1.0f - remaining_fraction;
804 return (transmitted > FLT_EPSILON) ? 1.0f / transmitted : 1.0f;
805}
806
807static inline size_t _rgb_index(const size_t pixel, const int channel)
808{
809 return 4 * pixel + channel;
810}
811
826static int _simulate_channel(const dt_iop_crystgrain_runtime_t *const rt, const float *const image, float *const result,
827 float *const remaining, float *const exposure)
828{
829 const int width = rt->width;
830 const int height = rt->height;
831 const size_t npixels = (size_t)width * height;
832 float predicted_remaining = 1.0f;
833 memset(result, 0, sizeof(float) * npixels);
834 memcpy(remaining, image, sizeof(float) * npixels);
835
836 for(int layer = 0; layer < rt->layers; layer++)
837 {
839 const uint64_t layer_seed = rt->base_seed + layer * 4099u;
840 if(_build_layer_kernel_bank(kernel_bank, rt, layer_seed) != 0) return 1;
841 predicted_remaining = fmaxf(predicted_remaining
842 - _predict_layer_capture(kernel_bank, rt->layer_scale, predicted_remaining),
843 0.0f);
844
845 for(int y = 0; y < rt->height; y++)
846 {
847 const int world_y = (int)((rt->roi_y + y) * rt->inv_scale);
848 for(int x = 0; x < rt->width; x++)
849 {
850 const size_t index = (size_t)y * rt->width + x;
851 if(remaining[index] <= 0.0f) continue;
852
853 const int world_x = (int)((rt->roi_x + x) * rt->inv_scale);
854 const uint64_t pixel_seed = rt->base_seed
855 ^ ((uint64_t)(uint32_t)world_x << 32)
856 ^ (uint32_t)world_y
857 ^ (uint64_t)(layer + 1) * 0x9e3779b97f4a7c15ull;
858 const int kernel_index = splitmix32(pixel_seed ^ 0x94d049bb133111ebull) & (DT_CRYSTGRAIN_LAYER_KERNELS - 1);
859 const dt_iop_crystgrain_layer_kernel_t *const entry = &kernel_bank[kernel_index];
860 const dt_iop_crystgrain_kernel_t *const kernel = &entry->footprint;
861 const int radius = kernel->radius;
862 const int interior = (y >= radius && y < rt->height - radius && x >= radius && x < rt->width - radius);
863
864 // How many crystals the germ process actually dropped on this pixel.
865 // At and near 100% a crystal is wider than a pixel, the intensity is
866 // well below one and this behaves like the old Bernoulli draw. Zoomed
867 // out, several sub-pixel crystals share the same pixel, and sampling
868 // that count — instead of truncating it at one — is precisely what
869 // makes the grain average out with resolution the way a real emulsion
870 // does when it is scanned smaller.
871 const int crystals = _poisson_random(pixel_seed ^ 0xda942042e4dd58b5ull, entry->intensity);
872 if(crystals <= 0) continue;
873
874 if(kernel->count == 1 && kernel->dx[0] == 0 && kernel->dy[0] == 0)
875 {
876 // Sub-pixel regime: the whole crystal fits in its own pixel, so the
877 // coincident crystals only ever deplete that one pixel and the
878 // recurrence has a closed form.
879 const float alpha = kernel->alpha[0];
880 const float cap = image[index] * alpha * rt->layer_scale;
881 const float deposited = _coincident_capture(remaining[index], cap, alpha, crystals);
882 if(deposited <= 0.0f) continue;
883
884 result[index] += deposited;
885 remaining[index] = fmaxf(remaining[index] - deposited, 0.0f);
886 continue;
887 }
888
889 // Each pixel sweeps only its own crystal footprint to print one flat
890 // tone into the reconstruction while depleting the remaining light
891 // field in place. Coincident crystals are printed one after the other,
892 // each seeing what the previous ones left behind.
893 for(int crystal = 0; crystal < crystals; crystal++)
894 {
895 float seed_energy = 0.0f;
896 float original_energy = 0.0f;
897
898 for(int tap = 0; tap < kernel->count; tap++)
899 {
900 int xx = x + kernel->dx[tap];
901 int yy = y + kernel->dy[tap];
902 if(!interior)
903 {
904 xx = _reflect_index(xx, width);
905 yy = _reflect_index(yy, height);
906 }
907
908 const size_t dst = (size_t)yy * width + xx;
909 // A crystal prints one flat tone from the average of the current
910 // light field and of the immutable input over the whole grain
911 // surface, so no detail finer than the grain survives inside it.
912 seed_energy += remaining[dst] * kernel->alpha[tap];
913 original_energy += image[dst] * kernel->alpha[tap];
914 }
915 seed_energy /= kernel->area;
916 // The user layer scale applies to the whole grain surface, so the
917 // per-pixel flat tone cap must scale with the grain area too.
918 original_energy *= rt->layer_scale;
919 seed_energy = fminf(seed_energy, original_energy);
920 if(seed_energy <= 0.0f) break;
921
922 for(int tap = 0; tap < kernel->count; tap++)
923 {
924 int xx = x + kernel->dx[tap];
925 int yy = y + kernel->dy[tap];
926 if(!interior)
927 {
928 xx = _reflect_index(xx, width);
929 yy = _reflect_index(yy, height);
930 }
931
932 const size_t dst = (size_t)yy * width + xx;
933 // Write the flat crystal tone back to the output and subtract the
934 // same quantity from the light field that will feed deeper layers.
935 const float deposited = seed_energy * kernel->alpha[tap];
936 result[dst] += deposited;
937 remaining[dst] = fmaxf(remaining[dst] - deposited, 0.0f);
938 }
939 }
940 }
941 }
942
943 _free_layer_kernel_bank(kernel_bank);
944 }
945
946 *exposure = _predict_stack_exposure(predicted_remaining);
947 return 0;
948}
949
963 float *const exposure)
964{
965 const int width = rt->width;
966 const int height = rt->height;
967 const size_t npixels = (size_t)width * height;
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 };
971 const uint64_t channel_salt[3] = {
972 0xa24baed4963ee407ull,
973 0x9fb21c651e98df25ull,
974 0xc13fa9a902a6328full
975 };
976
977 memset(state->result, 0, sizeof(float) * npixels * 4);
978 memcpy(state->remaining, state->image, sizeof(float) * npixels * 4);
979
980 for(int layer = 0; layer < rt->layers; layer++)
981 {
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);
985 const uint64_t layer_seed = rt->base_seed + (uint64_t)(sublayer + 1) * 4099u;
986 if(_build_layer_kernel_bank(kernel_bank, rt, layer_seed) != 0) return 1;
987 predicted_remaining[c] = fmaxf(predicted_remaining[c]
988 - _predict_layer_capture(kernel_bank, rt->layer_scale, predicted_remaining[c]),
989 0.0f);
990
991 for(int y = 0; y < height; y++)
992 {
993 const int world_y = (int)((rt->roi_y + y) * rt->inv_scale);
994 for(int x = 0; x < width; x++)
995 {
996 const size_t index = (size_t)y * width + x;
997 const float remaining_total = state->remaining[_rgb_index(index, 0)]
998 + state->remaining[_rgb_index(index, 1)]
999 + state->remaining[_rgb_index(index, 2)];
1000 if(remaining_total <= 0.0f) continue;
1001
1002 const int world_x = (int)((rt->roi_x + x) * rt->inv_scale);
1003 const uint64_t shared_seed = rt->base_seed
1004 ^ ((uint64_t)(uint32_t)world_x << 32)
1005 ^ (uint32_t)world_y
1006 ^ (uint64_t)(sublayer + 1) * 0x9e3779b97f4a7c15ull;
1007 const uint64_t channel_seed = shared_seed ^ channel_salt[c];
1008 const int use_shared = _uniform_random(channel_seed ^ 0x4f1bbcdc6762f96bull) < rt->channel_correlation;
1009 const uint64_t pixel_seed = use_shared ? shared_seed : channel_seed;
1010 const int kernel_index = splitmix32(pixel_seed ^ 0x94d049bb133111ebull) & (DT_CRYSTGRAIN_LAYER_KERNELS - 1);
1011 const dt_iop_crystgrain_layer_kernel_t *const entry = &kernel_bank[kernel_index];
1012 const dt_iop_crystgrain_kernel_t *const kernel = &entry->footprint;
1013 const int radius = kernel->radius;
1014 const int interior = (y >= radius && y < height - radius && x >= radius && x < width - radius);
1015
1016 // Same germ count as the monochrome path, drawn per spectral sub-stack.
1017 const int crystals = _poisson_random(pixel_seed ^ 0xda942042e4dd58b5ull, entry->intensity);
1018 if(crystals <= 0) continue;
1019
1020 if(kernel->count == 1 && kernel->dx[0] == 0 && kernel->dy[0] == 0)
1021 {
1022 const float alpha = kernel->alpha[0];
1023 const float cap = state->image[_rgb_index(index, c)] * alpha * rt->layer_scale;
1024 const float deposited = _coincident_capture(state->remaining[_rgb_index(index, c)], cap, alpha, crystals);
1025 if(deposited <= 0.0f) continue;
1026
1027 state->result[_rgb_index(index, c)] += deposited;
1028 state->remaining[_rgb_index(index, c)]
1029 = fmaxf(state->remaining[_rgb_index(index, c)] - deposited, 0.0f);
1030 continue;
1031 }
1032
1033 for(int crystal = 0; crystal < crystals; crystal++)
1034 {
1035 float seed_energy = 0.0f;
1036 float original_energy = 0.0f;
1037
1038 for(int tap = 0; tap < kernel->count; tap++)
1039 {
1040 int xx = x + kernel->dx[tap];
1041 int yy = y + kernel->dy[tap];
1042 if(!interior)
1043 {
1044 xx = _reflect_index(xx, width);
1045 yy = _reflect_index(yy, height);
1046 }
1047
1048 const size_t dst = (size_t)yy * width + xx;
1049 // Each depth layer belongs to one spectral emulsion only, so it
1050 // prints one flat tone from that channel and leaves the others to
1051 // deeper layers.
1052 seed_energy += state->remaining[_rgb_index(dst, c)] * kernel->alpha[tap];
1053 original_energy += state->image[_rgb_index(dst, c)] * kernel->alpha[tap];
1054 }
1055
1056 seed_energy /= kernel->area;
1057 original_energy *= rt->layer_scale;
1058 const float captured = fminf(seed_energy, original_energy);
1059 if(captured <= 0.0f) break;
1060
1061 for(int tap = 0; tap < kernel->count; tap++)
1062 {
1063 int xx = x + kernel->dx[tap];
1064 int yy = y + kernel->dy[tap];
1065 if(!interior)
1066 {
1067 xx = _reflect_index(xx, width);
1068 yy = _reflect_index(yy, height);
1069 }
1070
1071 const size_t dst = (size_t)yy * width + xx;
1072 const float deposited = captured * kernel->alpha[tap];
1073 state->result[_rgb_index(dst, c)] += deposited;
1074 state->remaining[_rgb_index(dst, c)]
1075 = fmaxf(state->remaining[_rgb_index(dst, c)] - deposited, 0.0f);
1076 }
1077 }
1078 }
1079 }
1080
1081 _free_layer_kernel_bank(kernel_bank);
1082 }
1083
1084 for(int c = 0; c < 3; c++) exposure[c] = _predict_stack_exposure(predicted_remaining[c]);
1085 return 0;
1086}
1087
1095static void _extract_luminance_kernel(const float *const restrict in, float *const restrict image,
1096 const int width, const int height,
1097 const dt_iop_order_iccprofile_info_t *const work_profile)
1098{
1100 for(int y = 0; y < height; y++)
1101 {
1102 const size_t row = (size_t)y * width;
1103 for(int x = 0; x < width; x++)
1104 {
1105 const size_t k = row + x;
1106 const float luminance = (work_profile)
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,
1109 work_profile->nonlinearlut)
1110 : dt_camera_rgb_luminance(in + 4 * k);
1111
1112 image[k] = fmaxf(luminance, 0.0f);
1113 }
1114 }
1115}
1116
1125static void _extract_rgb_kernels(const float *const restrict in, float *const restrict image,
1126 const int width, const int height)
1127{
1129 for(int y = 0; y < height; y++)
1130 {
1131 const size_t row = (size_t)y * width;
1132 for(int x = 0; x < width; x++)
1133 {
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);
1138
1139 image[_rgb_index(k, 0)] = red;
1140 image[_rgb_index(k, 1)] = green;
1141 image[_rgb_index(k, 2)] = blue;
1142 image[_rgb_index(k, 3)] = 0.0f;
1143 }
1144 }
1145}
1146
1158static void _apply_mono_grain_kernel(const float *const restrict in, float *const restrict out,
1159 const float *const restrict image, const float *const restrict result,
1160 const int width, const int height, const float exposure)
1161{
1163 for(int y = 0; y < height; y++)
1164 {
1165 const size_t row = (size_t)y * width;
1166 for(int x = 0; x < width; x++)
1167 {
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;
1171
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);
1175 }
1176 }
1177}
1178
1188static void _finalize_color_grain_kernel(const float *const restrict in, float *const restrict out,
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)
1192{
1194 for(int y = 0; y < height; y++)
1195 {
1196 const size_t row = (size_t)y * width;
1197 for(int x = 0; x < width; x++)
1198 {
1199 const size_t k = row + x;
1200 const float image_r = image[_rgb_index(k, 0)];
1201 const float image_g = image[_rgb_index(k, 1)];
1202 const float image_b = image[_rgb_index(k, 2)];
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;
1210
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;
1214 }
1215 }
1216}
1217
1218#ifdef HAVE_OPENCL
1219#define DT_CRYSTGRAIN_CL_PROGRAM 36
1220#define DT_CRYSTGRAIN_REDUCESIZE 64
1221
1237static cl_int _upload_layer_bank(const int devid, const dt_iop_crystgrain_layer_kernel_t *const bank,
1238 cl_mem *const dev_params, cl_mem *const dev_alpha)
1239{
1240 float params[DT_CRYSTGRAIN_LAYER_KERNELS][4];
1241 size_t total_taps = 0;
1242
1243 *dev_params = NULL;
1244 *dev_alpha = NULL;
1245
1246 for(int i = 0; i < DT_CRYSTGRAIN_LAYER_KERNELS; i++)
1247 {
1248 const int radius = bank[i].footprint.radius;
1249 const size_t stride = (size_t)(2 * radius + 1);
1250 params[i][0] = bank[i].intensity;
1251 params[i][1] = bank[i].footprint.area;
1252 params[i][2] = (float)radius;
1253 params[i][3] = (float)total_taps;
1254 total_taps += stride * stride;
1255 }
1256
1257 float *const alpha = calloc(total_taps, sizeof(float));
1258 if(IS_NULL_PTR(alpha)) return CL_MEM_OBJECT_ALLOCATION_FAILURE;
1259
1260 for(int i = 0; i < DT_CRYSTGRAIN_LAYER_KERNELS; i++)
1261 {
1262 const dt_iop_crystgrain_kernel_t *const footprint = &bank[i].footprint;
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];
1266
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];
1270 }
1271
1272 *dev_params = dt_opencl_copy_host_to_device_constant(devid, sizeof(params), params);
1273 *dev_alpha = dt_opencl_alloc_device_buffer(devid, sizeof(float) * total_taps);
1274 cl_int err = (IS_NULL_PTR(*dev_params) || IS_NULL_PTR(*dev_alpha)) ? CL_MEM_OBJECT_ALLOCATION_FAILURE : CL_SUCCESS;
1275
1276 if(err == CL_SUCCESS)
1277 err = dt_opencl_write_buffer_to_device(devid, alpha, *dev_alpha, 0, sizeof(float) * total_taps, TRUE);
1278
1279 free(alpha);
1280
1281 if(err != CL_SUCCESS)
1282 {
1283 dt_opencl_release_mem_object(*dev_params);
1284 dt_opencl_release_mem_object(*dev_alpha);
1285 *dev_params = NULL;
1286 *dev_alpha = NULL;
1287 }
1288
1289 return err;
1290}
1291
1301static int _simulate_channel_cl(const int devid, dt_iop_crystgrain_global_data_t *const gd,
1302 const dt_iop_crystgrain_runtime_t *const rt, cl_mem dev_image, cl_mem dev_result,
1303 cl_mem dev_remaining, float *const exposure)
1304{
1305 cl_int err = CL_SUCCESS;
1306 const int width = rt->width;
1307 const int height = rt->height;
1308 size_t sizes[3] = { ROUNDUP((size_t)width, (size_t)16), ROUNDUP((size_t)height, (size_t)16), 1 };
1309 const size_t buffer_size = sizeof(float) * (size_t)width * height;
1310 float predicted_remaining = 1.0f;
1311
1312 dt_opencl_set_kernel_arg(devid, gd->kernel_zero_scalar, 0, sizeof(cl_mem), &dev_result);
1313 dt_opencl_set_kernel_arg(devid, gd->kernel_zero_scalar, 1, sizeof(int), &width);
1314 dt_opencl_set_kernel_arg(devid, gd->kernel_zero_scalar, 2, sizeof(int), &height);
1315 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_zero_scalar, sizes);
1316 if(err != CL_SUCCESS) return err;
1317
1318 err = dt_opencl_enqueue_copy_buffer_to_buffer(devid, dev_image, dev_remaining, 0, 0, buffer_size);
1319 if(err != CL_SUCCESS) return err;
1320
1321 for(int layer = 0; layer < rt->layers; layer++)
1322 {
1324 cl_mem dev_kernel_bank = NULL;
1325 cl_mem dev_kernel_alpha = NULL;
1326 const float layer_scale = rt->layer_scale;
1327 const int roi_x = rt->roi_x;
1328 const int roi_y = rt->roi_y;
1329 const float inv_scale = rt->inv_scale;
1330 const cl_ulong base_seed = (cl_ulong)rt->base_seed;
1331
1332 if(_build_layer_kernel_bank(kernel_bank, rt, rt->base_seed + layer * 4099u) != 0)
1333 {
1334 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
1335 return err;
1336 }
1337 predicted_remaining = fmaxf(predicted_remaining
1338 - _predict_layer_capture(kernel_bank, rt->layer_scale, predicted_remaining),
1339 0.0f);
1340
1341 err = _upload_layer_bank(devid, kernel_bank, &dev_kernel_bank, &dev_kernel_alpha);
1342 _free_layer_kernel_bank(kernel_bank);
1343 if(err != CL_SUCCESS) return err;
1344
1345 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer, 0, sizeof(cl_mem), &dev_image);
1346 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer, 1, sizeof(cl_mem), &dev_remaining);
1347 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer, 2, sizeof(cl_mem), &dev_result);
1348 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer, 3, sizeof(cl_mem), &dev_kernel_bank);
1349 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer, 4, sizeof(int), &width);
1350 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer, 5, sizeof(int), &height);
1351 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer, 6, sizeof(int), &roi_x);
1352 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer, 7, sizeof(int), &roi_y);
1353 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer, 8, sizeof(float), &inv_scale);
1354 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer, 9, sizeof(cl_ulong), &base_seed);
1355 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer, 10, sizeof(int), &layer);
1356 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer, 11, sizeof(float), &layer_scale);
1357 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer, 12, sizeof(cl_mem), &dev_kernel_alpha);
1358 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_simulate_layer, sizes);
1359 dt_opencl_release_mem_object(dev_kernel_bank);
1360 dt_opencl_release_mem_object(dev_kernel_alpha);
1361 if(err != CL_SUCCESS) return err;
1362 }
1363
1364 *exposure = _predict_stack_exposure(predicted_remaining);
1365 return err;
1366}
1367
1369{
1371 module->data = gd;
1372 const int program = DT_CRYSTGRAIN_CL_PROGRAM;
1373 gd->kernel_zero_scalar = dt_opencl_create_kernel(program, "crystgrain_zero_scalar");
1374 gd->kernel_zero_rgb = dt_opencl_create_kernel(program, "crystgrain_zero_rgb");
1375 gd->kernel_extract_luminance = dt_opencl_create_kernel(program, "crystgrain_extract_luminance");
1376 gd->kernel_extract_rgb = dt_opencl_create_kernel(program, "crystgrain_extract_rgb");
1377 gd->kernel_simulate_layer = dt_opencl_create_kernel(program, "crystgrain_simulate_layer");
1378 gd->kernel_simulate_layer_color = dt_opencl_create_kernel(program, "crystgrain_simulate_layer_color");
1379 gd->kernel_apply_mono = dt_opencl_create_kernel(program, "crystgrain_apply_mono");
1380 gd->kernel_finalize_color = dt_opencl_create_kernel(program, "crystgrain_finalize_color");
1381}
1382
1397
1398int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece,
1399 cl_mem dev_in, cl_mem dev_out)
1400{
1401 const dt_iop_roi_t *const roi_in = &piece->roi_in;
1402 const dt_iop_roi_t *const roi_out = &piece->roi_out;
1403 const dt_iop_crystgrain_data_t *const d = (const dt_iop_crystgrain_data_t *)piece->data;
1406 const int devid = pipe->devid;
1407 const int width = roi_out->width;
1408 const int height = roi_out->height;
1409 // Grain size is authored in full-resolution output pixels at 100% zoom.
1410 // The current processing grid may already be downsampled twice:
1411 // 1. by the ROI zoom factor used for the current preview/export,
1412 // 2. by the mipmap level chosen before the pipe even starts.
1413 // Clamped at 1 so zooming past the native scale never invents larger
1414 // crystals than the ones the slider describes.
1415 const float kernel_scale = CLAMPS(1.0f / dt_dev_get_module_scale(pipe, roi_in), 1e-6f, 1.0f);
1416 cl_int err = CL_SUCCESS;
1417 float exposure[3] = { 1.0f, 1.0f, 1.0f };
1418
1419 if(width <= 0 || height <= 0 || d->layers <= 0 || d->filling <= 0.0f)
1420 {
1421 size_t origin[] = { 0, 0, 0 };
1422 size_t region[] = { (size_t)width, (size_t)height, 1 };
1423 return dt_opencl_enqueue_copy_image(devid, dev_in, dev_out, origin, origin, region);
1424 }
1425
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;
1432 dt_colorspaces_iccprofile_info_cl_t *profile_info_cl = NULL;
1433 cl_float *profile_lut_cl = NULL;
1434 cl_mem dev_profile_info = NULL;
1435 cl_mem dev_profile_lut = NULL;
1436
1437 dev_image = dt_opencl_alloc_device_buffer(devid, sizeof(float) * (size_t)width * height);
1438 dev_result = dt_opencl_alloc_device_buffer(devid, sizeof(float) * (size_t)width * height);
1439 dev_remaining = dt_opencl_alloc_device_buffer(devid, sizeof(float) * (size_t)width * height);
1440 if(IS_NULL_PTR(dev_image) || IS_NULL_PTR(dev_result) || IS_NULL_PTR(dev_remaining))
1441 {
1442 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
1443 goto error;
1444 }
1445
1447 .width = width,
1448 .height = height,
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,
1459 // The seed is keyed on the image alone, deliberately not on the buffer
1460 // size: crystal sizes are drawn in full-resolution pixels and seeds are
1461 // addressed by full-resolution position, so keeping the seed free of the
1462 // grid dimensions is what lets the same crystals land in the same places
1463 // whatever resolution the pipeline runs at.
1464 .base_seed = ((uint64_t)_hash_string(pipe->dev->image_storage.filename) << 32)
1465 ^ 0x9e3779b97f4a7c15ull
1466 };
1467 const float current_surface = _average_discrete_grain_surface(&rt);
1468 // Neutral layer capture is defined as 1/layers of the input energy for a
1469 // grain of average rasterized surface. Since each sampled bank entry can
1470 // have a different discrete area A_i, the flat-field recurrence uses
1471 // min(r_l, A_i * layer_scale) per crystal, with the current_surface term
1472 // keeping the user-facing EV control centered on that neutral 1/layers
1473 // behaviour across preview scales.
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;
1477
1478 size_t sizes[3] = { ROUNDUP((size_t)width, (size_t)16), ROUNDUP((size_t)height, (size_t)16), 1 };
1479
1480 if(d->mode == DT_CRYSTGRAIN_MONO)
1481 {
1482 err = dt_ioppr_build_iccprofile_params_cl(work_profile, devid, &profile_info_cl, &profile_lut_cl,
1483 &dev_profile_info, &dev_profile_lut);
1484 if(err != CL_SUCCESS) goto error;
1485
1486 const int use_work_profile = (!IS_NULL_PTR(work_profile)) ? 1 : 0;
1487 dt_opencl_set_kernel_arg(devid, gd->kernel_extract_luminance, 0, sizeof(cl_mem), &dev_in);
1488 dt_opencl_set_kernel_arg(devid, gd->kernel_extract_luminance, 1, sizeof(cl_mem), &dev_image);
1489 dt_opencl_set_kernel_arg(devid, gd->kernel_extract_luminance, 2, sizeof(int), &width);
1490 dt_opencl_set_kernel_arg(devid, gd->kernel_extract_luminance, 3, sizeof(int), &height);
1491 dt_opencl_set_kernel_arg(devid, gd->kernel_extract_luminance, 4, sizeof(cl_mem), &dev_profile_info);
1492 dt_opencl_set_kernel_arg(devid, gd->kernel_extract_luminance, 5, sizeof(cl_mem), &dev_profile_lut);
1493 dt_opencl_set_kernel_arg(devid, gd->kernel_extract_luminance, 6, sizeof(int), &use_work_profile);
1495 if(err != CL_SUCCESS) goto error;
1496
1497 err = _simulate_channel_cl(devid, gd, &rt, dev_image, dev_result, dev_remaining, &exposure[0]);
1498 if(err != CL_SUCCESS) goto error;
1499
1500 dt_opencl_set_kernel_arg(devid, gd->kernel_apply_mono, 0, sizeof(cl_mem), &dev_in);
1501 dt_opencl_set_kernel_arg(devid, gd->kernel_apply_mono, 1, sizeof(cl_mem), &dev_image);
1502 dt_opencl_set_kernel_arg(devid, gd->kernel_apply_mono, 2, sizeof(cl_mem), &dev_result);
1503 dt_opencl_set_kernel_arg(devid, gd->kernel_apply_mono, 3, sizeof(cl_mem), &dev_out);
1504 dt_opencl_set_kernel_arg(devid, gd->kernel_apply_mono, 4, sizeof(int), &width);
1505 dt_opencl_set_kernel_arg(devid, gd->kernel_apply_mono, 5, sizeof(int), &height);
1506 dt_opencl_set_kernel_arg(devid, gd->kernel_apply_mono, 6, sizeof(float), &exposure[0]);
1507 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_apply_mono, sizes);
1508 goto error;
1509 }
1510
1511 dev_image_rgb = dt_opencl_alloc_device_buffer(devid, sizeof(float) * (size_t)width * height * 4);
1512 dev_result_rgb = dt_opencl_alloc_device_buffer(devid, sizeof(float) * (size_t)width * height * 4);
1513 dev_remaining_rgb = dt_opencl_alloc_device_buffer(devid, sizeof(float) * (size_t)width * height * 4);
1514 if(IS_NULL_PTR(dev_image_rgb) || IS_NULL_PTR(dev_result_rgb) || IS_NULL_PTR(dev_remaining_rgb))
1515 {
1516 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
1517 goto error;
1518 }
1519
1520 dt_opencl_set_kernel_arg(devid, gd->kernel_extract_rgb, 0, sizeof(cl_mem), &dev_in);
1521 dt_opencl_set_kernel_arg(devid, gd->kernel_extract_rgb, 1, sizeof(cl_mem), &dev_image_rgb);
1522 dt_opencl_set_kernel_arg(devid, gd->kernel_extract_rgb, 2, sizeof(int), &width);
1523 dt_opencl_set_kernel_arg(devid, gd->kernel_extract_rgb, 3, sizeof(int), &height);
1524 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_extract_rgb, sizes);
1525 if(err != CL_SUCCESS) goto error;
1526
1527 const size_t color_buffer_size = sizeof(float) * (size_t)width * height * 4;
1528 dt_opencl_set_kernel_arg(devid, gd->kernel_zero_rgb, 0, sizeof(cl_mem), &dev_result_rgb);
1529 dt_opencl_set_kernel_arg(devid, gd->kernel_zero_rgb, 1, sizeof(int), &width);
1530 dt_opencl_set_kernel_arg(devid, gd->kernel_zero_rgb, 2, sizeof(int), &height);
1531 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_zero_rgb, sizes);
1532 if(err != CL_SUCCESS) goto error;
1533
1534 err = dt_opencl_enqueue_copy_buffer_to_buffer(devid, dev_image_rgb, dev_remaining_rgb, 0, 0, color_buffer_size);
1535 if(err != CL_SUCCESS) goto error;
1536
1537 float predicted_remaining[3] = { 1.0f, 1.0f, 1.0f };
1538 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer_color, 0, sizeof(cl_mem), &dev_image_rgb);
1539 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer_color, 1, sizeof(cl_mem), &dev_remaining_rgb);
1540 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer_color, 2, sizeof(cl_mem), &dev_result_rgb);
1541 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer_color, 3, sizeof(int), &width);
1542 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer_color, 4, sizeof(int), &height);
1543 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer_color, 5, sizeof(int), &rt.roi_x);
1544 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer_color, 6, sizeof(int), &rt.roi_y);
1545 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer_color, 7, sizeof(float), &rt.inv_scale);
1546 {
1547 const cl_ulong base_seed = (cl_ulong)rt.base_seed;
1548 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer_color, 8, sizeof(cl_ulong), &base_seed);
1549 }
1550 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer_color, 10, sizeof(float), &rt.layer_scale);
1552
1553 for(int layer = 0; layer < rt.layers; layer++)
1554 {
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)
1560 ? layer
1561 : ((active_channel == 1) ? layer - blue_layers : layer - blue_layers - green_layers);
1562 if(_build_layer_kernel_bank(kernel_bank, &rt, rt.base_seed + (uint64_t)(sublayer + 1) * 4099u) != 0)
1563 {
1564 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
1565 goto error;
1566 }
1567 predicted_remaining[active_channel]
1568 = fmaxf(predicted_remaining[active_channel]
1569 - _predict_layer_capture(kernel_bank, rt.layer_scale, predicted_remaining[active_channel]),
1570 0.0f);
1571
1572 err = _upload_layer_bank(devid, kernel_bank, &dev_kernel_bank, &dev_kernel_alpha);
1573 _free_layer_kernel_bank(kernel_bank);
1574 if(err != CL_SUCCESS) goto error;
1575
1576 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer_color, 9, sizeof(cl_mem), &dev_kernel_bank);
1577 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer_color, 11, sizeof(int), &sublayer);
1578 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer_color, 12, sizeof(int), &active_channel);
1579 dt_opencl_set_kernel_arg(devid, gd->kernel_simulate_layer_color, 14, sizeof(cl_mem), &dev_kernel_alpha);
1581 dt_opencl_release_mem_object(dev_kernel_bank);
1582 dt_opencl_release_mem_object(dev_kernel_alpha);
1583 if(err != CL_SUCCESS) goto error;
1584 }
1585
1586 for(int c = 0; c < 3; c++) exposure[c] = _predict_stack_exposure(predicted_remaining[c]);
1587
1588 dt_opencl_set_kernel_arg(devid, gd->kernel_finalize_color, 0, sizeof(cl_mem), &dev_in);
1589 dt_opencl_set_kernel_arg(devid, gd->kernel_finalize_color, 1, sizeof(cl_mem), &dev_image_rgb);
1590 dt_opencl_set_kernel_arg(devid, gd->kernel_finalize_color, 2, sizeof(cl_mem), &dev_result_rgb);
1591 dt_opencl_set_kernel_arg(devid, gd->kernel_finalize_color, 3, sizeof(cl_mem), &dev_out);
1592 dt_opencl_set_kernel_arg(devid, gd->kernel_finalize_color, 4, sizeof(int), &width);
1593 dt_opencl_set_kernel_arg(devid, gd->kernel_finalize_color, 5, sizeof(int), &height);
1594 dt_opencl_set_kernel_arg(devid, gd->kernel_finalize_color, 6, sizeof(float), &exposure[0]);
1595 dt_opencl_set_kernel_arg(devid, gd->kernel_finalize_color, 7, sizeof(float), &exposure[1]);
1596 dt_opencl_set_kernel_arg(devid, gd->kernel_finalize_color, 8, sizeof(float), &exposure[2]);
1597 dt_opencl_set_kernel_arg(devid, gd->kernel_finalize_color, 9, sizeof(float), &d->colorspace_saturation);
1598 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_finalize_color, sizes);
1599
1600error:
1602 dt_opencl_release_mem_object(dev_result);
1603 dt_opencl_release_mem_object(dev_remaining);
1604 dt_opencl_release_mem_object(dev_image_rgb);
1605 dt_opencl_release_mem_object(dev_result_rgb);
1606 dt_opencl_release_mem_object(dev_remaining_rgb);
1607 dt_ioppr_free_iccprofile_params_cl(&profile_info_cl, &profile_lut_cl, &dev_profile_info, &dev_profile_lut);
1608 return (err == CL_SUCCESS) ? TRUE : FALSE;
1609}
1610#endif
1611
1614{
1617
1618 d->mode = p->mode;
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;
1626}
1627
1633
1635{
1636 dt_free_align(piece->data);
1637 piece->data = NULL;
1638}
1639
1640int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece,
1641 const void *const ivoid, void *const ovoid)
1642{
1643 const dt_iop_roi_t *const roi_in = &piece->roi_in;
1644 const dt_iop_roi_t *const roi_out = &piece->roi_out;
1645 const dt_iop_crystgrain_data_t *const d = (const dt_iop_crystgrain_data_t *)piece->data;
1647 const float *const restrict in = (const float *const)ivoid;
1648 float *const restrict out = (float *const)ovoid;
1649 const int width = roi_out->width;
1650 const int height = roi_out->height;
1651 // Grain size is authored in full-resolution output pixels at 100% zoom, and
1652 // clamped there — see the OpenCL path for the details.
1653 const float kernel_scale = CLAMPS(1.0f / dt_dev_get_module_scale(pipe, roi_in), 1e-6f, 1.0f);
1654
1655 if(width <= 0 || height <= 0 || d->layers <= 0 || d->filling <= 0.0f)
1656 {
1657 dt_iop_copy_image_roi(out, in, 4, roi_in, roi_out, TRUE);
1658 return 0;
1659 }
1660
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;
1667 if(dt_iop_alloc_image_buffers(self, roi_in, roi_out,
1668 1, &image,
1669 1 | DT_IMGSZ_CLEARBUF, &result,
1670 1, &remaining,
1671 4, &image_rgb,
1672 4 | DT_IMGSZ_CLEARBUF, &result_rgb,
1673 4, &remaining_rgb,
1674 0))
1675 {
1676 dt_iop_copy_image_roi(out, in, 4, roi_in, roi_out, TRUE);
1677 return 1;
1678 }
1679
1681
1683 .width = width,
1684 .height = height,
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,
1695 // The seed is keyed on the image alone, deliberately not on the buffer
1696 // size: crystal sizes are drawn in full-resolution pixels and seeds are
1697 // addressed by full-resolution position, so keeping the seed free of the
1698 // grid dimensions is what lets the same crystals land in the same places
1699 // whatever resolution the pipeline runs at.
1700 .base_seed = ((uint64_t)_hash_string(pipe->dev->image_storage.filename) << 32)
1701 ^ 0x9e3779b97f4a7c15ull
1702 };
1703 const float current_surface = _average_discrete_grain_surface(&rt);
1704 // Neutral layer capture is defined as 1/layers of the input energy for a
1705 // grain of average rasterized surface. Since each sampled bank entry can
1706 // have a different discrete area A_i, the flat-field recurrence uses
1707 // min(r_l, A_i * layer_scale) per crystal, with the current_surface term
1708 // keeping the user-facing EV control centered on that neutral 1/layers
1709 // behaviour across preview scales.
1710 rt.layer_scale = d->layer_capture / MAX((float)d->layers, 1.0f) / MAX(current_surface, FLT_EPSILON);
1711
1712 if(d->mode == DT_CRYSTGRAIN_MONO)
1713 {
1714 _extract_luminance_kernel(in, image, width, height, work_profile);
1715 float mono_exposure = 1.0f;
1716
1717 if(_simulate_channel(&rt, image, result, remaining, &mono_exposure) != 0)
1718 {
1722 dt_iop_copy_image_roi(out, in, 4, roi_in, roi_out, TRUE);
1723 return 1;
1724 }
1725
1726 _apply_mono_grain_kernel(in, out, image, result, width, height, mono_exposure);
1727 }
1728 else
1729 {
1730 // Color film layers share one crystal geometry stack. Keep the working
1731 // light fields interleaved as RGB tuples so extraction, simulation and
1732 // final write-back all walk one contiguous color buffer instead of three
1733 // independent scalar plates.
1734 _extract_rgb_kernels(in, image_rgb, width, height);
1735 float color_exposure[3] = { 1.0f, 1.0f, 1.0f };
1736 const dt_iop_crystgrain_color_state_t color_state = {
1737 .image = image_rgb,
1738 .result = result_rgb,
1739 .remaining = remaining_rgb
1740 };
1741
1742 if(_simulate_color(&rt, &color_state, color_exposure) != 0)
1743 {
1749 dt_pixelpipe_cache_free_align(remaining_rgb);
1750 dt_iop_copy_image_roi(out, in, 4, roi_in, roi_out, TRUE);
1751 return 1;
1752 }
1753
1754 _finalize_color_grain_kernel(in, out, image_rgb, result_rgb, width, height,
1755 color_exposure[0], color_exposure[1], color_exposure[2],
1756 d->colorspace_saturation);
1757 }
1758
1764 dt_pixelpipe_cache_free_align(remaining_rgb);
1765 return 0;
1766}
1767
1768void gui_update(struct dt_iop_module_t *self)
1769{
1772 const gboolean is_color = (p->mode == DT_CRYSTGRAIN_COLOR);
1773
1774 gtk_widget_set_visible(g->channel_correlation, is_color);
1775 gtk_widget_set_visible(g->colorspace_saturation, is_color);
1776}
1777
1778static void _mode_changed(GtkWidget *widget, dt_iop_module_t *self)
1779{
1780 gui_update(self);
1781}
1782
1783void gui_init(struct dt_iop_module_t *self)
1784{
1786
1787 g->mode = dt_bauhaus_combobox_from_params(self, "mode");
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);
1790
1791 g->filling = dt_bauhaus_slider_from_params(self, "filling");
1792 dt_bauhaus_slider_set_format(g->filling, "%");
1793 gtk_widget_set_tooltip_text(g->filling, _("surface ratio occupied by silver-halide crystals in each layer"));
1794
1795 g->grain_size = dt_bauhaus_slider_from_params(self, "grain_size");
1796 dt_bauhaus_slider_set_digits(g->grain_size, 0);
1797 dt_bauhaus_slider_set_format(g->grain_size, " px");
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"));
1799
1800 g->layers = dt_bauhaus_slider_from_params(self, "layers");
1801 dt_bauhaus_slider_set_digits(g->layers, 0);
1802 gtk_widget_set_tooltip_text(g->layers, _("number of crystal layers stacked through the emulsion"));
1803
1804 g->layer_capture = dt_bauhaus_slider_from_params(self, "layer_capture");
1805 dt_bauhaus_slider_set_soft_range(g->layer_capture, -2.0f, 2.0f);
1806 dt_bauhaus_slider_set_format(g->layer_capture, _(" EV"));
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"));
1808
1809 g->channel_correlation = dt_bauhaus_slider_from_params(self, "channel_correlation");
1810 dt_bauhaus_slider_set_format(g->channel_correlation, "%");
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"));
1812
1813 g->colorspace_saturation = dt_bauhaus_slider_from_params(self, "colorspace_saturation");
1814 dt_bauhaus_slider_set_format(g->colorspace_saturation, "%");
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"));
1816
1817 g->size_stddev = dt_bauhaus_slider_from_params(self, "size_stddev");
1818 gtk_widget_set_tooltip_text(g->size_stddev, _("log-normal standard deviation of crystal sizes"));
1819
1820 gui_update(self);
1821}
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_bauhaus_slider_set_soft_range(GtkWidget *widget, float soft_min, float soft_max)
Definition bauhaus.c:1498
void dt_bauhaus_slider_set_digits(GtkWidget *widget, int val)
Definition bauhaus.c:3343
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3407
static float dt_camera_rgb_luminance(const float4 rgb)
@ IOP_CS_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.
static const float x
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
const float max
const dt_colormatrix_t dt_aligned_pixel_t out
static const int row
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.
Definition crystgrain.c:357
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.
Definition crystgrain.c:435
const char ** description(struct dt_iop_module_t *self)
Definition crystgrain.c:160
int default_group()
Definition crystgrain.c:174
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.
Definition crystgrain.c:770
#define DT_CRYSTGRAIN_CL_PROGRAM
#define DT_CRYSTGRAIN_LAYER_KERNELS
Definition crystgrain.c:48
static float _uniform_random(const uint64_t seed)
Turn a 64-bit seed into a uniform random number in [0; 1).
Definition crystgrain.c:237
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’`.
Definition crystgrain.c:260
static float _average_grain_surface(const dt_iop_crystgrain_runtime_t *const rt)
Estimate the reference grain surface used to normalize layer capture.
Definition crystgrain.c:601
static size_t _rgb_index(const size_t pixel, const int channel)
Definition crystgrain.c:807
static __DT_CLONE_TARGETS__ unsigned int _hash_string(const char *s)
Hash a string into a stable 32-bit seed.
Definition crystgrain.c:227
static void _free_layer_kernel_bank(dt_iop_crystgrain_layer_kernel_t *const bank)
Release all crystal footprints from one layer bank.
Definition crystgrain.c:680
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.
Definition crystgrain.c:546
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
Definition crystgrain.c:53
const char * name()
Definition crystgrain.c:155
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...
Definition crystgrain.c:298
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.
Definition crystgrain.c:826
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.
Definition crystgrain.c:658
static float _coincident_capture(const float remaining, const float cap, const float alpha, const int count)
Deplete one pixel with count coincident crystals.
Definition crystgrain.c:736
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.
Definition crystgrain.c:315
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)
Definition crystgrain.c:179
int flags()
Definition crystgrain.c:169
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.
Definition crystgrain.c:385
static float _predict_stack_exposure(const float remaining_fraction)
Predict the exposure compensation of one monochrome grain stack.
Definition crystgrain.c:801
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)
Definition crystgrain.c:184
static float _gaussian_random(const uint64_t seed_a, const uint64_t seed_b)
Turn 2 seeds into one gaussian deviate.
Definition crystgrain.c:249
static float _polygon_area(const float radius_f, const float vertices)
Analytic surface of one crystal, in current grid pixels squared.
Definition crystgrain.c:349
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)
dt_iop_crystgrain_mode_t
Definition crystgrain.c:56
@ DT_CRYSTGRAIN_COLOR
Definition crystgrain.c:58
@ DT_CRYSTGRAIN_MONO
Definition crystgrain.c:57
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.
Definition crystgrain.c:622
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.
Definition crystgrain.c:961
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.
Definition crystgrain.c:703
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params, const int new_version)
Definition crystgrain.c:209
#define DT_CRYSTGRAIN_SUBSAMPLES
Definition crystgrain.c:366
#define M_PI_F
static unsigned int splitmix32(const unsigned long seed)
void dt_iop_params_t
Definition dev_history.h:43
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)
Definition eaw.c:29
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,...)
Definition imagebuf.c:35
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)
Definition imagebuf.c:163
#define DT_IMGSZ_CLEARBUF
Definition imagebuf.h:62
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)
Definition imagebuf.h:91
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)
Definition imageop.c:1893
float dt_dev_get_module_scale(const dt_dev_pixelpipe_t *const pipe, const dt_iop_roi_t *const roi_in)
Definition imageop.c:134
@ IOP_FLAGS_INCLUDE_IN_STYLES
Definition imageop.h:185
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:186
@ IOP_GROUP_EFFECTS
Definition imageop.h:161
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...
Definition imageop_gui.h:81
#define IOP_GUI_ALLOC(module)
Definition imageop_gui.h:93
void *const ovoid
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...
Definition macros.h:96
static const int max_size
Definition map.c:132
#define CLAMPS(A, L, H)
Definition math.h:78
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
Definition mem_alloc.h:214
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
Definition mem_alloc.h:225
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
#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)
Definition opencl.c:2554
void * dt_opencl_alloc_device_buffer(const int devid, const size_t size)
Definition opencl.c:2970
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2448
void * dt_opencl_copy_host_to_device_constant(const int devid, const size_t size, void *host)
Definition opencl.c:2750
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)
Definition opencl.c:2738
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)
Definition opencl.c:2679
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2491
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
Definition opencl.c:2545
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)
Definition opencl.c:2714
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2805
#define ROUNDUP(a, n)
Definition opencl.h:82
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
#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...
Definition simd.h:55
const float uint32_t state[4]
return noise *sigma mu
const float u2
unsigned __int64 uint64_t
Definition strptime.c:75
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
dt_image_t image_storage
Definition develop.h:225
char filename[DT_MAX_FILENAME_LEN]
Definition image.h:386
dt_iop_crystgrain_mode_t mode
Definition crystgrain.c:87
GtkWidget * colorspace_saturation
Definition crystgrain.c:82
dt_iop_crystgrain_kernel_t footprint
Definition crystgrain.c:110
dt_iop_crystgrain_mode_t mode
Definition crystgrain.c:63
dt_dev_operation_t op
Definition imageop.h:235
dt_iop_global_data_t * data
Definition imageop.h:238
dt_iop_global_data_t * global_data
Definition imageop.h:337
dt_iop_params_t * params
Definition imageop.h:333
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.
Definition format.h:49
int width
Definition format.h:50
int height
Definition format.h:50
#define __DT_CLONE_TARGETS__
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29