Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
iop_order.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2018-2021, 2023, 2025-2026 Aurélien PIERRE.
4 Copyright (C) 2018-2019 Edgardo Hoszowski.
5 Copyright (C) 2019, 2021 Aldric Renaudin.
6 Copyright (C) 2019 Andreas Schneider.
7 Copyright (C) 2019, 2021 Hanno Schwalm.
8 Copyright (C) 2019 Heiko Bauke.
9 Copyright (C) 2019 Jacques Le Clerc.
10 Copyright (C) 2019 jakubfi.
11 Copyright (C) 2019 luzpaz.
12 Copyright (C) 2019-2022 Pascal Obry.
13 Copyright (C) 2019-2020 Philippe Weyland.
14 Copyright (C) 2019, 2021 Sakari Kapanen.
15 Copyright (C) 2019 Tobias Ellinghaus.
16 Copyright (C) 2020 rawfiner.
17 Copyright (C) 2021 Anna.
18 Copyright (C) 2021 Hubert Kowalski.
19 Copyright (C) 2021 Ralf Brown.
20 Copyright (C) 2022 Martin Bařinka.
21 Copyright (C) 2025 Guillaume Stutin.
22
23 darktable is free software: you can redistribute it and/or modify
24 it under the terms of the GNU General Public License as published by
25 the Free Software Foundation, either version 3 of the License, or
26 (at your option) any later version.
27
28 darktable is distributed in the hope that it will be useful,
29 but WITHOUT ANY WARRANTY; without even the implied warranty of
30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 GNU General Public License for more details.
32
33 You should have received a copy of the GNU General Public License
34 along with darktable. If not, see <http://www.gnu.org/licenses/>.
35*/
36#ifdef HAVE_CONFIG_H
37#include "config.h"
38#endif
39
40#include "common/darktable.h"
41#include "common/iop_order.h"
42#include "common/styles.h"
43#include "common/debug.h"
44#include "common/deprecations.h"
45#include "common/image.h"
46#include "common/image_cache.h"
47#include "develop/imageop.h"
48#include "develop/pixelpipe.h"
49
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53
54#define DT_IOP_ORDER_VERSION 5
55
56#define DT_IOP_ORDER_INFO (darktable.unmuted & DT_DEBUG_IOPORDER)
57
58static void _ioppr_reset_iop_order(GList *iop_order_list);
59static dt_iop_order_entry_t *dt_ioppr_get_iop_order_entry(GList *iop_order_list, const char *op_name,
60 const int multi_priority);
61static gboolean dt_ioppr_write_iop_order(const dt_iop_order_t kind, GList *iop_order_list, const int32_t imgid);
63static void dt_ioppr_update_for_entries(dt_develop_t *dev, GList *entry_list, gboolean append);
64static void dt_ioppr_check_duplicate_iop_order(GList **_iop_list, GList *history_list);
65static void dt_ioppr_migrate_iop_order(struct dt_develop_t *dev, const int32_t imgid);
66static GList *dt_ioppr_extract_multi_instances_list(GList *iop_order_list);
67static GList *dt_ioppr_merge_multi_instance_iop_order_list(GList *iop_order_list, GList *multi_instance_list);
68static gboolean _ioppr_sanity_check_iop_order(GList *list);
69
70const char *iop_order_string[] =
71{
72 N_("custom"),
73 N_("legacy"),
74 N_("v3.0 RAW"),
75 N_("v3.0 JPEG"),
76 N_("Ansel RAW"),
77 N_("Ansel JPEG")
78};
79
80const char *dt_iop_order_string(const dt_iop_order_t order)
81{
82 if(order >= DT_IOP_ORDER_LAST)
83 return "???";
84 else
85 return iop_order_string[order];
86}
87
88// note legacy_order & v30_order have the original iop-order double that is
89// used only for the initial database migration.
90//
91// in the new code only the iop-order as int is used to order the module on the GUI.
92
93// @@_NEW_MODULE: For new module it is required to insert the new module name in both lists below.
94
96 { { 0.5f }, "basebuffer", 0},
97 { { 1.0f }, "rawprepare", 0},
98 { { 2.0f }, "invert", 0},
99 { { 3.0f }, "temperature", 0},
100 { { 4.0f }, "highlights", 0},
101 { { 5.0f }, "cacorrect", 0},
102 { { 6.0f }, "hotpixels", 0},
103 { { 7.0f }, "rawdenoise", 0},
104 { { 8.0f }, "demosaic", 0},
105 { { 8.5f }, "detailmask", 0},
106 { { 9.0f }, "mask_manager", 0},
107 { {10.0f }, "denoiseprofile", 0},
108 { {11.0f }, "tonemap", 0},
109 { {12.0f }, "exposure", 0},
110 { {13.0f }, "spots", 0},
111 { {14.0f }, "retouch", 0},
112 { {15.0f }, "lens", 0},
113 { {15.5f }, "cacorrectrgb", 0},
114 { {15.5f }, "initialscale", 0},
115 { {16.0f }, "ashift", 0},
116 { {17.0f }, "liquify", 0},
117 { {18.0f }, "rotatepixels", 0},
118 { {19.0f }, "scalepixels", 0},
119 { {20.0f }, "flip", 0},
120 { {21.0f }, "clipping", 0},
121 { {21.5f }, "toneequal", 0},
122 { {21.7f }, "crop", 0},
123 { {22.0f }, "graduatednd", 0},
124 { {23.0f }, "basecurve", 0},
125 { {24.0f }, "bilateral", 0},
126 { {25.0f }, "profile_gamma", 0},
127 { {26.0f }, "hazeremoval", 0},
128 { {27.0f }, "colorin", 0},
129 { {27.5f }, "channelmixerrgb", 0},
130 { {27.5f }, "diffuse", 0},
131 { {27.5f }, "censorize", 0},
132 { {27.5f }, "negadoctor", 0},
133 { {27.5f }, "blurs", 0},
134 { {27.5f }, "basicadj", 0},
135 { {28.0f }, "colorreconstruct", 0},
136 { {29.0f }, "colorchecker", 0},
137 { {30.0f }, "defringe", 0},
138 { {31.0f }, "equalizer", 0},
139 { {32.0f }, "vibrance", 0},
140 { {33.0f }, "colorbalance", 0},
141 { {33.4f }, "splittoningrgb", 0},
142 { {33.45f }, "colorprimaries", 0},
143 { {33.5f }, "colorbalancergb", 0},
144 { {33.6f }, "colorequal", 0},
145 { {33.7f }, "drawlayer", 0},
146 { {34.0f }, "colorize", 0},
147 { {36.0f }, "colormapping", 0},
148 { {37.0f }, "bloom", 0},
149 { {38.0f }, "nlmeans", 0},
150 { {39.0f }, "globaltonemap", 0},
151 { {40.0f }, "shadhi", 0},
152 { {41.0f }, "atrous", 0},
153 { {42.0f }, "bilat", 0},
154 { {43.0f }, "colorzones", 0},
155 { {44.0f }, "lowlight", 0},
156 { {45.0f }, "monochrome", 0},
157 { {46.0f }, "filmic", 0},
158 { {46.25f }, "crystgrain", 0},
159 { {46.5f }, "filmicrgb", 0},
160 { {47.0f }, "colisa", 0},
161 { {48.0f }, "zonesystem", 0},
162 { {49.0f }, "tonecurve", 0},
163 { {50.0f }, "levels", 0},
164 { {50.2f }, "rgblevels", 0},
165 { {50.5f }, "rgbcurve", 0},
166 { {51.0f }, "relight", 0},
167 { {52.0f }, "colorcorrection", 0},
168 { {53.0f }, "sharpen", 0},
169 { {54.0f }, "lowpass", 0},
170 { {55.0f }, "highpass", 0},
171 { {56.0f }, "grain", 0},
172 { {56.5f }, "lut3d", 0},
173 { {57.0f }, "colorcontrast", 0},
174 { {58.0f }, "colorout", 0},
175 { {59.0f }, "channelmixer", 0},
176 { {60.0f }, "soften", 0},
177 { {61.0f }, "vignette", 0},
178 { {62.0f }, "splittoning", 0},
179 { {63.0f }, "velvia", 0},
180 { {65.0f }, "finalscale", 0},
181 { {66.0f }, "overexposed", 0},
182 { {67.0f }, "rawoverexposed", 0},
183 { {67.5f }, "dither", 0},
184 { {68.0f }, "borders", 0},
185 { {69.0f }, "watermark", 0},
186 { {71.0f }, "gamma", 0},
187 { { 0.0f }, "", 0}
188};
189
190// default order for RAW files, assumed to be linear from start
192 { { 0.5 }, "basebuffer", 0},
193 { { 1.0 }, "rawprepare", 0},
194 { { 2.0 }, "invert", 0},
195 { { 3.0f }, "temperature", 0},
196 { { 4.0f }, "highlights", 0},
197 { { 5.0f }, "cacorrect", 0},
198 { { 6.0f }, "hotpixels", 0},
199 { { 7.0f }, "rawdenoise", 0},
200 { { 8.0f }, "demosaic", 0},
201 { { 8.5f }, "detailmask", 0},
202 { { 9.0f }, "denoiseprofile", 0},
203 { {10.0f }, "bilateral", 0},
204 { {11.0f }, "rotatepixels", 0},
205 { {12.0f }, "scalepixels", 0},
206 { {13.0f }, "lens", 0},
207 { {13.5f }, "cacorrectrgb", 0}, // correct chromatic aberrations after lens correction so that lensfun
208 // does not reintroduce chromatic aberrations when trying to correct them
209 { {14.0f }, "hazeremoval", 0},
210 { {14.0f }, "initialscale", 0},
211 { {15.0f }, "ashift", 0},
212 { {16.0f }, "flip", 0},
213 { {17.0f }, "clipping", 0},
214 { {18.0f }, "liquify", 0},
215 { {19.0f }, "spots", 0},
216 { {20.0f }, "retouch", 0},
217 { {21.0f }, "exposure", 0},
218 { {22.0f }, "mask_manager", 0},
219 { {23.0f }, "tonemap", 0},
220 { {24.0f }, "toneequal", 0}, // last module that need enlarged roi_in
221 { {24.5f }, "crop", 0}, // should go after all modules that may need a wider roi_in
222 { {25.0f }, "graduatednd", 0},
223 { {26.0f }, "profile_gamma", 0},
224 { {28.0f }, "colorin", 0},
225 { {28.5f }, "channelmixerrgb", 0},
226 { {28.5f }, "diffuse", 0},
227 { {28.5f }, "censorize", 0},
228 { {28.5f }, "negadoctor", 0}, // Cineon film encoding comes after scanner input color profile
229 { {28.5f }, "blurs", 0}, // physically-accurate blurs (motion and lens)
230 { {29.0f }, "nlmeans", 0}, // signal processing (denoising)
231 // -> needs a signal as scene-referred as possible (even if it works in Lab)
232 { {30.0f }, "colorchecker", 0}, // calibration to "neutral" exchange colour space
233 // -> improve colour calibration of colorin and reproductibility
234 // of further edits (styles etc.)
235 { {31.0f }, "defringe", 0}, // desaturate fringes in Lab, so needs properly calibrated colours
236 // in order for chromaticity to be meaningful,
237 { {32.0f }, "atrous", 0}, // frequential operation, needs a signal as scene-referred as possible to avoid halos
238 { {33.0f }, "lowpass", 0}, // same
239 { {34.0f }, "highpass", 0}, // same
240 { {35.0f }, "sharpen", 0}, // same, worst than atrous in same use-case, less control overall
241
242 { {37.0f }, "colortransfer", 0}, // probably better if source and destination colours are neutralized in the same
243 // colour exchange space, hence after colorin and colorcheckr,
244 // but apply after frequential ops in case it does non-linear witchcraft,
245 // just to be safe
246 { {38.0f }, "colormapping", 0}, // same
247 { {39.0f }, "channelmixer", 0}, // does exactly the same thing as colorin, aka RGB to RGB matrix conversion,
248 // but coefs are user-defined instead of calibrated and read from ICC profile.
249 // Really versatile yet under-used module, doing linear ops,
250 // very good in scene-referred workflow
251 { {40.0f }, "basicadj", 0}, // module mixing view/model/control at once, usage should be discouraged
252 { {41.0f }, "colorbalance", 0}, // scene-referred color manipulation
253 { {41.4f }, "splittoningrgb", 0}, // keyed CAT16 plus RGB mixer before primary warping
254 { {41.45f }, "colorprimaries", 0}, // editable RGB/CYM primary nodes in dt UCS
255 { {41.5f }, "colorbalancergb", 0}, // scene-referred color manipulation
256 { {41.6f }, "colorequal", 0}, // dynamic hue-defined RGB stretching around the achromatic axis
257 { {41.7f }, "drawlayer", 0}, // TIFF-backed paint layers in scene-referred RGB
258 { {42.0f }, "rgbcurve", 0}, // really versatile way to edit colour in scene-referred and display-referred workflow
259 { {43.0f }, "rgblevels", 0}, // same
260 { {44.0f }, "basecurve", 0}, // conversion from scene-referred to display referred, reverse-engineered
261 // on camera JPEG default look
262 { {45.0f }, "filmic", 0}, // same, but different (parametric) approach
263 { {45.5f }, "crystgrain", 0}, // scene-referred grain, before filmic RGB
264 { {46.0f }, "filmicrgb", 0}, // same, upgraded
265 { {36.0f }, "lut3d", 0}, // apply a creative style or film emulation, possibly non-linear
266 { {47.0f }, "colisa", 0}, // edit contrast while damaging colour
267 { {48.0f }, "tonecurve", 0}, // same
268 { {49.0f }, "levels", 0}, // same
269 { {50.0f }, "shadhi", 0}, // same
270 { {51.0f }, "zonesystem", 0}, // same
271 { {52.0f }, "globaltonemap", 0}, // same
272 { {53.0f }, "relight", 0}, // flatten local contrast while pretending do add lightness
273 { {54.0f }, "bilat", 0}, // improve clarity/local contrast after all the bad things we have done
274 // to it with tonemapping
275 { {55.0f }, "colorcorrection", 0}, // now that the colours have been damaged by contrast manipulations,
276 // try to recover them - global adjustment of white balance for shadows and highlights
277 { {56.0f }, "colorcontrast", 0}, // adjust chrominance globally
278 { {57.0f }, "velvia", 0}, // same
279 { {58.0f }, "vibrance", 0}, // same, but more subtle
280 { {60.0f }, "colorzones", 0}, // same, but locally
281 { {61.0f }, "bloom", 0}, // creative module
282 { {62.0f }, "colorize", 0}, // creative module
283 { {63.0f }, "lowlight", 0}, // creative module
284 { {64.0f }, "monochrome", 0}, // creative module
285 { {65.0f }, "grain", 0}, // creative module
286 { {66.0f }, "soften", 0}, // creative module
287 { {67.0f }, "splittoning", 0}, // creative module
288 { {68.0f }, "vignette", 0}, // creative module
289 { {69.0f }, "colorreconstruct", 0},// try to salvage blown areas before ICC intents in LittleCMS2 do things with them.
290 { {70.0f }, "colorout", 0},
291 { {72.0f }, "finalscale", 0},
292 { {73.0f }, "overexposed", 0},
293 { {74.0f }, "rawoverexposed", 0},
294 { {75.0f }, "dither", 0},
295 { {76.0f }, "borders", 0},
296 { {77.0f }, "watermark", 0},
297 { {78.0f }, "gamma", 0},
298 { { 0.0f }, "", 0 }
299};
300
301// default order for JPEG/TIFF/PNG files, non-linear before colorin
303 // the following modules are not used anyway for non-RAW images :
304 { { 0.5 }, "basebuffer", 0 },
305 { { 1.0 }, "rawprepare", 0 },
306 { { 2.0 }, "invert", 0 },
307 { { 3.0f }, "temperature", 0 },
308 { { 4.0f }, "highlights", 0 },
309 { { 5.0f }, "cacorrect", 0 },
310 { { 6.0f }, "hotpixels", 0 },
311 { { 7.0f }, "rawdenoise", 0 },
312 { { 8.0f }, "demosaic", 0 },
313 { { 8.5f }, "detailmask", 0 },
314 // all the modules between [8; 28] expect linear RGB, so they need to be moved after colorin
315 { { 28.0f }, "colorin", 0 },
316 // moved modules : (copy-pasted in the same order)
317 { { 28.0f }, "denoiseprofile", 0},
318 { { 28.0f }, "bilateral", 0},
319 { { 28.0f }, "rotatepixels", 0},
320 { { 28.0f }, "scalepixels", 0},
321 { { 28.0f }, "lens", 0},
322 { { 28.0f }, "cacorrectrgb", 0}, // correct chromatic aberrations after lens correction so that lensfun
323 // does not reintroduce chromatic aberrations when trying to correct them
324 { { 28.0f }, "hazeremoval", 0},
325 { { 28.0f }, "initialscale", 0 },
326 { { 28.0f }, "ashift", 0},
327 { { 28.0f }, "flip", 0},
328 { { 28.0f }, "clipping", 0},
329 { { 28.0f }, "liquify", 0},
330 { { 28.0f }, "spots", 0},
331 { { 28.0f }, "retouch", 0},
332 { { 28.0f }, "exposure", 0},
333 { { 28.0f }, "mask_manager", 0},
334 { { 28.0f }, "tonemap", 0},
335 { { 28.0f }, "toneequal", 0}, // last module that need enlarged roi_in
336 { { 28.0f }, "crop", 0}, // should go after all modules that may need a wider roi_in
337 { { 28.0f }, "graduatednd", 0},
338 { { 28.0f }, "profile_gamma", 0},
339 // from there, it's the same as the raw order
340 { { 28.5f }, "channelmixerrgb", 0 },
341 { { 28.5f }, "diffuse", 0 },
342 { { 28.5f }, "censorize", 0 },
343 { { 28.5f }, "negadoctor", 0 }, // Cineon film encoding comes after scanner input color profile
344 { { 28.5f }, "blurs", 0 }, // physically-accurate blurs (motion and lens)
345 { { 29.0f }, "nlmeans", 0 }, // signal processing (denoising)
346 // -> needs a signal as scene-referred as possible (even if it works in Lab)
347 { { 30.0f }, "colorchecker", 0 }, // calibration to "neutral" exchange colour space
348 // -> improve colour calibration of colorin and reproductibility
349 // of further edits (styles etc.)
350 { { 31.0f }, "defringe", 0 }, // desaturate fringes in Lab, so needs properly calibrated colours
351 // in order for chromaticity to be meaningful,
352 { { 32.0f }, "atrous", 0 }, // frequential operation, needs a signal as scene-referred as possible to avoid halos
353 { { 33.0f }, "lowpass", 0 }, // same
354 { { 34.0f }, "highpass", 0 }, // same
355 { { 35.0f }, "sharpen", 0 }, // same, worst than atrous in same use-case, less control overall
356 { { 38.0f }, "colormapping", 0 }, // same
357 { { 39.0f }, "channelmixer", 0 }, // does exactly the same thing as colorin, aka RGB to RGB matrix conversion,
358 // but coefs are user-defined instead of calibrated and read from ICC
359 // profile. Really versatile yet under-used module, doing linear ops, very
360 // good in scene-referred workflow
361 { { 40.0f }, "basicadj", 0 }, // module mixing view/model/control at once, usage should be discouraged
362 { { 41.0f }, "colorbalance", 0 }, // scene-referred color manipulation
363 { { 41.4f }, "splittoningrgb", 0 }, // keyed CAT16 plus RGB mixer before primary warping
364 { { 41.45f }, "colorprimaries", 0 }, // editable RGB/CYM primary nodes in dt UCS
365 { { 41.5f }, "colorbalancergb", 0 }, // scene-referred color manipulation
366 { { 41.6f }, "colorequal", 0 }, // dynamic hue-defined RGB stretching around the achromatic axis
367 { { 41.7f }, "drawlayer", 0 }, // TIFF-backed paint layers in scene-referred RGB
368 { { 42.0f }, "rgbcurve", 0 }, // really versatile way to edit colour in scene-referred and display-referred
369 // workflow
370 { { 43.0f }, "rgblevels", 0 }, // same
371 { { 44.0f }, "basecurve", 0 }, // conversion from scene-referred to display referred, reverse-engineered
372 // on camera JPEG default look
373 { { 45.0f }, "filmic", 0 }, // same, but different (parametric) approach
374 { { 45.5f }, "crystgrain", 0 }, // scene-referred grain, before filmic RGB
375 { { 46.0f }, "filmicrgb", 0 }, // same, upgraded
376 { { 36.0f }, "lut3d", 0 }, // apply a creative style or film emulation, possibly non-linear
377 { { 47.0f }, "colisa", 0 }, // edit contrast while damaging colour
378 { { 48.0f }, "tonecurve", 0 }, // same
379 { { 49.0f }, "levels", 0 }, // same
380 { { 50.0f }, "shadhi", 0 }, // same
381 { { 51.0f }, "zonesystem", 0 }, // same
382 { { 52.0f }, "globaltonemap", 0 }, // same
383 { { 53.0f }, "relight", 0 }, // flatten local contrast while pretending do add lightness
384 { { 54.0f }, "bilat", 0 }, // improve clarity/local contrast after all the bad things we have done
385 // to it with tonemapping
386 { { 55.0f }, "colorcorrection", 0 }, // now that the colours have been damaged by contrast manipulations,
387 // try to recover them - global adjustment of white balance for shadows and
388 // highlights
389 { { 56.0f }, "colorcontrast", 0 }, // adjust chrominance globally
390 { { 57.0f }, "velvia", 0 }, // same
391 { { 58.0f }, "vibrance", 0 }, // same, but more subtle
392 { { 60.0f }, "colorzones", 0 }, // same, but locally
393 { { 61.0f }, "bloom", 0 }, // creative module
394 { { 62.0f }, "colorize", 0 }, // creative module
395 { { 63.0f }, "lowlight", 0 }, // creative module
396 { { 64.0f }, "monochrome", 0 }, // creative module
397 { { 65.0f }, "grain", 0 }, // creative module
398 { { 66.0f }, "soften", 0 }, // creative module
399 { { 67.0f }, "splittoning", 0 }, // creative module
400 { { 68.0f }, "vignette", 0 }, // creative module
401 { { 69.0f }, "colorreconstruct", 0 }, // try to salvage blown areas before ICC intents in LittleCMS2 do things
402 // with them.
403 { { 70.0f }, "colorout", 0 },
404 { { 72.0f }, "finalscale", 0 },
405 { { 73.0f }, "overexposed", 0 },
406 { { 74.0f }, "rawoverexposed", 0 },
407 { { 75.0f }, "dither", 0 },
408 { { 76.0f }, "borders", 0 },
409 { { 77.0f }, "watermark", 0 },
410 { { 78.0f }, "gamma", 0 },
411 { { 0.0f }, "", 0 }
412};
413
415 // RAW modules. Not used on JPG anyway
416 { { 0.5f}, "basebuffer", 0 },
417 { { 1.0f }, "rawprepare", 0 },
418 { { 2.0f }, "invert", 0 },
419 { { 3.0f }, "highlights", 0 },
420 { { 4.0f }, "cacorrect", 0 },
421 { { 5.0f }, "hotpixels", 0 },
422 { { 6.0f }, "rawdenoise", 0 },
423 { { 7.0f }, "demosaic", 0 },
424
425 // input color profile: undo RGB TRC/gamma/EOTF
426 { { 8.0f }, "colorin", 0 },
427
428 // so from there we are in "linear RGB", meaning there has probably been some tone curve applied
429 // on the pixels before saving to raster, but now we don't carry the uint8_t encoding
430
431 { { 9.0f }, "detailmask", 0 },
432 { { 10.0f }, "temperature", 0 },
433 { { 11.0f }, "denoiseprofile", 0},
434 { { 12.0f }, "bilateral", 0}, // RGB surface blur
435 { { 13.0f }, "rotatepixels", 0},
436 { { 14.0f }, "scalepixels", 0},
437 { { 15.0f }, "lens", 0},
438 { { 16.0f }, "cacorrectrgb", 0}, // correct chromatic aberrations after lens correction so that lensfun
439 // does not reintroduce chromatic aberrations when trying to correct them
440 { { 17.0f }, "hazeremoval", 0},
441 { { 18.0f }, "initialscale", 0 },
442 { { 19.0f }, "ashift", 0},
443 { { 20.0f }, "flip", 0},
444 { { 21.0f }, "clipping", 0},
445 { { 22.0f }, "liquify", 0},
446 { { 23.0f }, "spots", 0},
447 { { 24.0f }, "retouch", 0},
448 { { 25.0f }, "mask_manager", 0},
449
450 // Tone corrections
451 { { 26.0f }, "exposure", 0},
452 { { 27.0f }, "vignette", 0 }, // creative module but emulates lens vignetting, RGB, linear
453 { { 28.0f }, "graduatednd", 0},
454 { { 29.0f }, "toneequal", 0}, // last module that need enlarged roi_in
455 { { 30.0f }, "crop", 0}, // should go after all modules that may need a wider roi_in
456 { { 31.0f }, "profile_gamma", 0}, // shouldn't be needed for JPG
457
458 // from there, it's the same as the raw order
459
460 // Linear color handling
461 { { 32.0f }, "negadoctor", 0 }, // Cineon film encoding comes after scanner input color profile
462 { { 33.0f }, "channelmixerrgb", 0 }, // CAT & new channel mixer
463 { { 34.0f }, "channelmixer", 0 }, // Old channel mixer : used HSL...
464
465 // Linear convolutions
466 { { 35.0f }, "diffuse", 0 },
467 { { 36.0f }, "censorize", 0 },
468 { { 37.0f }, "blurs", 0 }, // physically-accurate blurs (motion and lens)
469
470 // Color work in RGB
471 { { 38.0f }, "basicadj", 0 }, // legacy shit duplicating features
472 { { 39.0f }, "splittoningrgb", 0 }, // keyed CAT16 plus RGB mixer before primary warping
473 { { 40.0f }, "colorprimaries", 0 }, // editable RGB/CYM primary nodes in dt UCS
474 { { 41.0f }, "colorbalance", 0 }, // scene-referred color manipulation
475 { { 42.0f }, "colorbalancergb", 0 }, // scene-referred color manipulation
476 { { 43.0f }, "colorequal", 0 }, // dynamic hue-defined RGB stretching around the achromatic axis
477 { { 44.0f }, "drawlayer", 0 }, // TIFF-backed paint layers in scene-referred RGB
478 { { 45.0f }, "crystgrain", 0 }, // scene-referred grain, before filmic RGB
479
480 // Interpolation for export pipelines: works better before non-linear transforms
481 { { 46.0f }, "finalscale", 0 },
482
483 { { 47.0f }, "tonemap", 0}, // shitty but at least it's unbounded RGB
484
485 // Display transforms: HDR -> SDR
486 { { 48.0f }, "filmic", 0 }, // same, but different (parametric) approach
487 { { 49.0f }, "filmicrgb", 0 }, // same, upgraded
488 { { 50.0f }, "basecurve", 0 }, // conversion from scene-referred to display referred, reverse-engineered
489 // on camera JPEG default look
490
491 // SDR modules :
492
493 // Wannabe signal-processing modules but they work in Lab so it's shit
494 { { 51.0f }, "nlmeans", 0 }, // denoise
495 { { 52.0f }, "defringe", 0 }, // desaturate fringes
496 { { 53.0f }, "bilat", 0 }, // local contrast
497 { { 54.0f }, "atrous", 0 }, // frequential operation, needs a signal as scene-referred as possible to avoid halos
498 { { 55.0f }, "lowpass", 0 }, // same
499 { { 56.0f }, "highpass", 0 }, // same
500 { { 57.0f }, "sharpen", 0 }, // same, worst than atrous in same use-case, less control overall
501
502 // RGB modules but don't support HDR white
503 { { 58.0f }, "lut3d", 0 },
504 { { 59.0f }, "rgbcurve", 0 },
505 { { 60.0f }, "rgblevels", 0 },
506 { { 61.0f }, "splittoning", 0 }, // HSL inside
507
508 // Lab color modules
509 { { 62.0f }, "colorchecker", 0 }, // calibration
510 { { 63.0f }, "colormapping", 0 }, // automagic shit. toy filter
511 { { 64.0f }, "colorcorrection", 0 }, // now that the colours have been damaged by contrast manipulations,
512 // try to recover them - global adjustment of white balance for shadows and
513 // highlights
514 { { 65.0f }, "colorcontrast", 0 }, // adjust chrominance globally
515 { { 66.0f }, "velvia", 0 }, // same
516 { { 67.0f }, "vibrance", 0 }, // same, but more subtle
517 { { 68.0f }, "colorzones", 0 }, // same, but locally
518
519 // Legacy Lab shit that should never have existed
520 { { 69.0f }, "colisa", 0 }, // contrast, lightness, saturation
521 { { 70.0f }, "tonecurve", 0 }, // same
522 { { 71.0f }, "levels", 0 }, // same
523 { { 72.0f }, "shadhi", 0 }, // same
524 { { 73.0f }, "zonesystem", 0 }, // same
525 { { 74.0f }, "globaltonemap", 0 },
526
527 // Lab toy filters
528 { { 75.0f }, "relight", 0 }, // tone EQ but worse
529 { { 76.0f }, "bloom", 0 }, // blurs but worse
530 { { 77.0f }, "colorize", 0 }, // somewhere between channel mixer and color balance
531 { { 78.0f }, "lowlight", 0 }, // simulate scotopic (night) vision
532 { { 79.0f }, "monochrome", 0 }, // channel mixer B&W mode but worse
533 { { 80.0f }, "grain", 0 }, // crystgrain but worse
534 { { 81.0f }, "soften", 0 }, // blurs but worse
535 { { 82.0f }, "colorreconstruct", 0 }, // try to salvage blown areas before ICC intents in LittleCMS2 do things
536 // with them.
537
538 // Display RGB from there
539 { { 83.0f }, "colorout", 0 },
540 { { 84.0f }, "overexposed", 0 },
541 { { 85.0f }, "rawoverexposed", 0 },
542
543 // Those 2 are shit because they internally rely on display RGB being sRGB
544 // Doesn't work for large gamut displays...
545 { { 86.0f }, "borders", 0 },
546 { { 87.0f }, "watermark", 0 },
547
548 // Hide quantization errors with noise
549 { { 88.0f }, "dither", 0 },
550
551 // Float to uint8 but only for darkroom pipelines.
552 // Also handles mask previews.
553 { { 89.0f }, "gamma", 0 },
554
555 { { 0.0f }, "", 0 }
556};
557
558// default order for RAW files, assumed to be linear from start
560 // RAW stuff
561 { { 0.0f }, "basebuffer", 0 },
562 { { 1.0f }, "rawprepare", 0},
563 { { 2.0f }, "invert", 0},
564 { { 3.0f }, "temperature", 0},
565 { { 4.0f }, "highlights", 0},
566 { { 5.0f }, "cacorrect", 0},
567 { { 6.0f }, "hotpixels", 0},
568 { { 7.0f }, "rawdenoise", 0},
569 { { 8.0f }, "demosaic", 0},
570
571 // Sensor RGB
572 { { 9.0f }, "denoiseprofile", 0},
573 { {10.0f }, "bilateral", 0},
574 { {11.0f }, "rotatepixels", 0},
575 { {12.0f }, "scalepixels", 0},
576 { {13.0f }, "detailmask", 0},
577 { {14.0f }, "lens", 0},
578 { {15.0f }, "cacorrectrgb", 0}, // correct chromatic aberrations after lens correction so that lensfun
579 // does not reintroduce chromatic aberrations when trying to correct them
580 { {16.0f }, "hazeremoval", 0},
581
582 { {17.0f }, "initialscale", 0},
583 { {18.0f }, "ashift", 0},
584 { {19.0f }, "flip", 0},
585 { {20.0f }, "clipping", 0},
586 { {21.0f }, "liquify", 0},
587 { {22.0f }, "spots", 0},
588 { {23.0f }, "retouch", 0},
589
590 // From there we support masking in modules
591 { {24.0f }, "mask_manager", 0},
592
593 // Linear tone corrections
594 { {25.0f }, "exposure", 0},
595 { {26.0f }, "vignette", 0 }, // creative module but emulates lens vignetting, RGB, linear
596 { {27.0f }, "graduatednd", 0},
597 { {28.0f }, "toneequal", 0}, // last module that need enlarged roi_in
598 { {29.0f }, "crop", 0}, // should go after all modules that may need a wider roi_in
599
600 // Needed by some very old cameras in RAW mode
601 { {30.0f }, "profile_gamma", 0},
602
603 { {31.0f }, "colorin", 0},
604
605 // from there, it's the same as the JPEG order
606
607 // Linear color handling
608 { { 32.0f }, "negadoctor", 0 }, // Cineon film encoding comes after scanner input color profile
609 { { 33.0f }, "channelmixerrgb", 0 }, // CAT & new channel mixer
610 { { 34.0f }, "channelmixer", 0 }, // Old channel mixer : used HSL...
611
612 // Linear convolutions
613 { { 35.0f }, "diffuse", 0 },
614 { { 36.0f }, "censorize", 0 },
615 { { 37.0f }, "blurs", 0 }, // physically-accurate blurs (motion and lens)
616
617 // Color work in RGB
618 { { 38.0f }, "basicadj", 0 }, // legacy shit duplicating features
619 { { 39.0f }, "splittoningrgb", 0 }, // keyed CAT16 plus RGB mixer before primary warping
620 { { 40.0f }, "colorprimaries", 0 }, // editable RGB/CYM primary nodes in dt UCS
621 { { 41.0f }, "colorbalance", 0 }, // scene-referred color manipulation
622 { { 42.0f }, "colorbalancergb", 0 }, // scene-referred color manipulation
623 { { 43.0f }, "colorequal", 0 }, // dynamic hue-defined RGB stretching around the achromatic axis
624 { { 44.0f }, "drawlayer", 0 }, // TIFF-backed paint layers in scene-referred RGB
625 { { 45.0f }, "crystgrain", 0 }, // scene-referred grain, before filmic RGB
626
627 // Interpolation for export pipelines: works better before non-linear transforms
628 { { 46.0f }, "finalscale", 0 },
629
630 { { 47.0f }, "tonemap", 0}, // shitty but at least it's unbounded RGB
631
632 // Display transforms: HDR -> SDR
633 { { 48.0f }, "filmic", 0 }, // same, but different (parametric) approach
634 { { 49.0f }, "filmicrgb", 0 }, // same, upgraded
635 { { 50.0f }, "basecurve", 0 }, // conversion from scene-referred to display referred, reverse-engineered
636 // on camera JPEG default look
637
638 // SDR modules :
639
640 // Wannabe signal-processing modules but they work in Lab so it's shit
641 { { 51.0f }, "nlmeans", 0 }, // denoise
642 { { 52.0f }, "defringe", 0 }, // desaturate fringes
643 { { 53.0f }, "bilat", 0 }, // local contrast
644 { { 54.0f }, "atrous", 0 }, // frequential operation, needs a signal as scene-referred as possible to avoid halos
645 { { 55.0f }, "lowpass", 0 }, // same
646 { { 56.0f }, "highpass", 0 }, // same
647 { { 57.0f }, "sharpen", 0 }, // same, worst than atrous in same use-case, less control overall
648
649 // RGB modules but don't support HDR white
650 { { 58.0f }, "lut3d", 0 },
651 { { 59.0f }, "rgbcurve", 0 },
652 { { 60.0f }, "rgblevels", 0 },
653 { { 61.0f }, "splittoning", 0 }, // HSL inside
654
655 // Lab color modules
656 { { 62.0f }, "colorchecker", 0 }, // calibration
657 { { 63.0f }, "colormapping", 0 }, // automagic shit. toy filter
658 { { 64.0f }, "colorcorrection", 0 }, // now that the colours have been damaged by contrast manipulations,
659 // try to recover them - global adjustment of white balance for shadows and
660 // highlights
661 { { 65.0f }, "colorcontrast", 0 }, // adjust chrominance globally
662 { { 66.0f }, "velvia", 0 }, // same
663 { { 67.0f }, "vibrance", 0 }, // same, but more subtle
664 { { 68.0f }, "colorzones", 0 }, // same, but locally
665
666 // Legacy Lab shit that should never have existed
667 { { 69.0f }, "colisa", 0 }, // contrast, lightness, saturation
668 { { 70.0f }, "tonecurve", 0 }, // same
669 { { 71.0f }, "levels", 0 }, // same
670 { { 72.0f }, "shadhi", 0 }, // same
671 { { 73.0f }, "zonesystem", 0 }, // same
672 { { 74.0f }, "globaltonemap", 0 },
673
674 // Lab toy filters
675 { { 75.0f }, "relight", 0 }, // tone EQ but worse
676 { { 76.0f }, "bloom", 0 }, // blurs but worse
677 { { 77.0f }, "colorize", 0 }, // somewhere between channel mixer and color balance
678 { { 78.0f }, "lowlight", 0 }, // simulate scotopic (night) vision
679 { { 79.0f }, "monochrome", 0 }, // channel mixer B&W mode but worse
680 { { 80.0f }, "grain", 0 }, // crystgrain but worse
681 { { 81.0f }, "soften", 0 }, // blurs but worse
682 { { 82.0f }, "colorreconstruct", 0 }, // try to salvage blown areas before ICC intents in LittleCMS2 do things
683 // with them.
684
685 // Display RGB from there
686 { { 83.0f }, "colorout", 0 },
687 { { 84.0f }, "overexposed", 0 },
688 { { 85.0f }, "rawoverexposed", 0 },
689
690 // Those 2 are shit because they internally rely on display RGB being sRGB
691 // Doesn't work for large gamut displays...
692 { { 86.0f }, "borders", 0 },
693 { { 87.0f }, "watermark", 0 },
694
695 // Hide quantization errors with noise
696 { { 88.0f }, "dither", 0 },
697
698 // Float to uint8 but only for darkroom pipelines.
699 // Also handles mask previews.
700 { { 89.0f }, "gamma", 0 },
701
702 { { 0.0f }, "", 0 }
703};
704
705static void *_dup_iop_order_entry(const void *src, gpointer data);
706static int _count_entries_operation(GList *e_list, const char *operation);
707
708
720static GList *_insert_before(GList *iop_order_list, const char *module, const char *new_module)
721{
722 gboolean exists = FALSE;
723
724 // First check that new module is missing
725 for(const GList *l = iop_order_list; l; l = g_list_next(l))
726 {
727 const dt_iop_order_entry_t *const restrict entry = (dt_iop_order_entry_t *)l->data;
728 if(!strcmp(entry->operation, new_module))
729 {
730 exists = TRUE;
731 break;
732 }
733 }
734
735 // Insert it if needed
736 if(!exists)
737 {
738 for(GList *l = iop_order_list; l; l = g_list_next(l))
739 {
740 dt_iop_order_entry_t *entry = (dt_iop_order_entry_t *)l->data;
741
742 if(!strcmp(entry->operation, module))
743 {
744 dt_iop_order_entry_t *new_entry = (dt_iop_order_entry_t *)malloc(sizeof(dt_iop_order_entry_t));
745
746 g_strlcpy(new_entry->operation, new_module, sizeof(new_entry->operation));
747 new_entry->instance = 0;
748 new_entry->o.iop_order = 0;
749
750 // Capture the returned pointer! g_list_insert_before may change the head.
751 iop_order_list = g_list_insert_before(iop_order_list, l, new_entry);
752 break;
753 }
754 }
755 }
756
757 return iop_order_list;
758}
759
760
762{
763 dt_iop_order_t iop_order_version = DT_IOP_ORDER_ANSEL_RAW;
764 gboolean has_stored_order = FALSE;
765
766 // check current iop order version
767 sqlite3_stmt *stmt;
768 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT version FROM main.module_order WHERE imgid = ?1",
769 -1, &stmt, NULL);
770 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
771 if(sqlite3_step(stmt) == SQLITE_ROW)
772 {
773 iop_order_version = sqlite3_column_int(stmt, 0);
774 has_stored_order = TRUE;
775 }
776 sqlite3_finalize(stmt);
777
778 if(!has_stored_order && imgid > 0 && !IS_NULL_PTR(darktable.image_cache))
779 {
780 const dt_image_t *image = dt_image_cache_testget(darktable.image_cache, imgid, 'r');
781 if(!IS_NULL_PTR(image))
782 {
785 }
786 }
787
788 return iop_order_version;
789}
790
791// a rule prevents operations to be switched,
792// that is a prev operation will not be allowed to be moved on top of the next operation.
794{
795 GList *rules = NULL;
796
797 const dt_iop_order_rule_t rule_entry[] = {
798 { .op_prev = "basebuffer", .op_next = "rawprepare" },
799 { .op_prev = "rawprepare", .op_next = "invert" },
800 { .op_prev = "invert", .op_next = "temperature" },
801 { .op_prev = "temperature", .op_next = "highlights" },
802 { .op_prev = "highlights", .op_next = "cacorrect" },
803 { .op_prev = "cacorrect", .op_next = "hotpixels" },
804 { .op_prev = "hotpixels", .op_next = "rawdenoise" },
805 { .op_prev = "rawdenoise", .op_next = "demosaic" },
806 { .op_prev = "demosaic", .op_next = "colorin" },
807 { .op_prev = "colorin", .op_next = "colorout" },
808 { .op_prev = "colorout", .op_next = "gamma" },
809 { .op_prev = "flip", .op_next = "crop" }, // crop GUI broken if flip is done on top
810 { .op_prev = "flip", .op_next = "clipping" }, // clipping GUI broken if flip is done on top
811 { .op_prev = "ashift", .op_next = "clipping" }, // clipping GUI broken if ashift is done on top
812 { .op_prev = "colorin", .op_next = "channelmixerrgb"},
813 { "\0", "\0" } };
814
815 int i = 0;
816 while(rule_entry[i].op_prev[0])
817 {
818 dt_iop_order_rule_t *rule = calloc(1, sizeof(dt_iop_order_rule_t));
819
820 memcpy(rule->op_prev, rule_entry[i].op_prev, sizeof(rule->op_prev));
821 memcpy(rule->op_next, rule_entry[i].op_next, sizeof(rule->op_next));
822
823 rules = g_list_prepend(rules, rule);
824 i++;
825 }
826
827 return g_list_reverse(rules); // list was built in reverse order, so un-reverse it
828}
829
830GList *dt_ioppr_get_iop_order_link(GList *iop_order_list, const char *op_name, const int multi_priority)
831{
832 GList *link = NULL;
833
834 for(GList *iops_order = iop_order_list; iops_order; iops_order = g_list_next(iops_order))
835 {
836 dt_iop_order_entry_t *order_entry = (dt_iop_order_entry_t *)iops_order->data;
837
838 if(strcmp(order_entry->operation, op_name) == 0
839 && (order_entry->instance == multi_priority || multi_priority == -1))
840 {
841 link = iops_order;
842 break;
843 }
844 }
845
846 return link;
847}
848
857static dt_iop_order_entry_t *dt_ioppr_get_iop_order_entry(GList *iop_order_list, const char *op_name,
858 const int multi_priority)
859{
860 const GList * const restrict link = dt_ioppr_get_iop_order_link(iop_order_list, op_name, multi_priority);
861 if(link)
862 return (dt_iop_order_entry_t *)link->data;
863 else
864 return NULL;
865}
866
867// returns the iop_order associated with the iop order entry that matches operation == op_name
868int dt_ioppr_get_iop_order(GList *iop_order_list, const char *op_name, const int multi_priority)
869{
870 int iop_order = INT_MAX;
871 const dt_iop_order_entry_t *const restrict order_entry =
872 dt_ioppr_get_iop_order_entry(iop_order_list, op_name, multi_priority);
873
874 if(order_entry)
875 {
876 iop_order = order_entry->o.iop_order;
877 }
878 else if(!dt_deprecated(op_name))
879 fprintf(stderr, "cannot get iop-order for %s instance %d\n", op_name, multi_priority);
880
881 return iop_order;
882}
883
884gint dt_sort_iop_list_by_order(gconstpointer a, gconstpointer b)
885{
886 const dt_iop_order_entry_t *const restrict am = (const dt_iop_order_entry_t *)a;
887 const dt_iop_order_entry_t *const restrict bm = (const dt_iop_order_entry_t *)b;
888 if(am->o.iop_order > bm->o.iop_order) return 1;
889 if(am->o.iop_order < bm->o.iop_order) return -1;
890 return 0;
891}
892
893gint dt_sort_iop_list_by_order_f(gconstpointer a, gconstpointer b)
894{
895 const dt_iop_order_entry_t *const restrict am = (const dt_iop_order_entry_t *)a;
896 const dt_iop_order_entry_t *const restrict bm = (const dt_iop_order_entry_t *)b;
897 if(am->o.iop_order_f > bm->o.iop_order_f) return 1;
898 if(am->o.iop_order_f < bm->o.iop_order_f) return -1;
899 return 0;
900}
901
904
906{
907 if(IS_NULL_PTR(iop_order_list)) return DT_IOP_ORDER_CUSTOM;
908
909 for(dt_iop_order_t version = DT_IOP_ORDER_LEGACY; version < DT_IOP_ORDER_LAST; version++)
910 {
911 int k = 0;
912 GList *l = iop_order_list;
913 gboolean ok = TRUE;
914 const dt_iop_order_entry_t *order = orders[version - DT_IOP_ORDER_LEGACY];
915
916 // Compare the incoming operation sequence against each built-in order. The
917 // array excludes DT_IOP_ORDER_CUSTOM, so the array index must be translated
918 // back to the enum value before returning the detected order kind.
919 while(l)
920 {
921 const dt_iop_order_entry_t *const restrict entry = (dt_iop_order_entry_t *)l->data;
922 if(strcmp(order[k].operation, entry->operation))
923 {
924 ok = FALSE;
925 break;
926 }
927 else
928 {
929 // skip all the other instance of same module if any
930 while(g_list_next(l)
931 && !strcmp(order[k].operation, ((dt_iop_order_entry_t *)(g_list_next(l)->data))->operation))
932 l = g_list_next(l);
933 }
934
935 k++;
936 l = g_list_next(l);
937 }
938
939 if(ok && order[k].operation[0] == '\0') return version;
940 }
941
942 return DT_IOP_ORDER_CUSTOM;
943}
944
945gboolean dt_ioppr_has_multiple_instances(GList *iop_order_list)
946{
947 GList *l = iop_order_list;
948
949 while(l)
950 {
951 GList *next = g_list_next(l);
952 if(next
953 && (strcmp(((dt_iop_order_entry_t *)(l->data))->operation,
954 ((dt_iop_order_entry_t *)(next->data))->operation) == 0))
955 {
956 return TRUE;
957 }
958 l = next;
959 }
960 return FALSE;
961}
962
974static gboolean dt_ioppr_write_iop_order(const dt_iop_order_t kind, GList *iop_order_list, const int32_t imgid)
975{
976 sqlite3_stmt *stmt;
977
979 "INSERT OR REPLACE INTO main.module_order VALUES (?1, 0, NULL)", -1,
980 &stmt, NULL);
981 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
982 if(sqlite3_step(stmt) != SQLITE_DONE) return FALSE;
983 sqlite3_finalize(stmt);
984
986 {
987 gchar *iop_list_txt = dt_ioppr_serialize_text_iop_order_list(iop_order_list);
989 "UPDATE main.module_order SET version = ?2, iop_list = ?3 WHERE imgid = ?1", -1,
990 &stmt, NULL);
991 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
993 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 3, iop_list_txt, -1, SQLITE_TRANSIENT);
994 if(sqlite3_step(stmt) != SQLITE_DONE) return FALSE;
995 sqlite3_finalize(stmt);
996
997 dt_free(iop_list_txt);
998 }
999 else
1000 {
1002 "UPDATE main.module_order SET version = ?2, iop_list = NULL WHERE imgid = ?1", -1,
1003 &stmt, NULL);
1004 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
1006 if(sqlite3_step(stmt) != SQLITE_DONE) return FALSE;
1007 sqlite3_finalize(stmt);
1008 }
1009
1010 return TRUE;
1011}
1012
1013gboolean dt_ioppr_write_iop_order_list(GList *iop_order_list, const int32_t imgid)
1014{
1016 return dt_ioppr_write_iop_order(kind, iop_order_list, imgid);
1017}
1018
1026{
1027 GList *iop_order_list = NULL;
1028 int k = 0;
1029 while(entries[k].operation[0])
1030 {
1032
1033 g_strlcpy(entry->operation, entries[k].operation, sizeof(entry->operation));
1034 entry->instance = 0;
1035 entry->o.iop_order_f = entries[k].o.iop_order_f;
1036 iop_order_list = g_list_prepend(iop_order_list, entry);
1037
1038 k++;
1039 }
1040
1041 return g_list_reverse(iop_order_list); // list was built in reverse order, so un-reverse it
1042}
1043
1045{
1046 GList *iop_order_list = NULL;
1047
1048 if(version == DT_IOP_ORDER_LEGACY)
1049 {
1050 iop_order_list = _table_to_list(legacy_order);
1051 }
1052 else if(version == DT_IOP_ORDER_V30)
1053 {
1054 iop_order_list = _table_to_list(v30_order);
1055 }
1056 else if(version == DT_IOP_ORDER_V30_JPG)
1057 {
1058 iop_order_list = _table_to_list(v30_jpg_order);
1059 }
1060 else if(version == DT_IOP_ORDER_ANSEL_RAW)
1061 {
1062 iop_order_list = _table_to_list(ansel_raw_order);
1063 }
1064 else if(version == DT_IOP_ORDER_ANSEL_JPG)
1065 {
1066 iop_order_list = _table_to_list(ansel_jpg_order);
1067 }
1068
1069
1070 return iop_order_list;
1071}
1072
1073gboolean dt_ioppr_has_iop_order_list(int32_t imgid)
1074{
1075 gboolean result = FALSE;
1076 sqlite3_stmt *stmt;
1077
1078 // clang-format off
1080 "SELECT version, iop_list"
1081 " FROM main.module_order"
1082 " WHERE imgid=?1", -1, &stmt, NULL);
1083 // clang-format on
1084 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
1085
1086 if(sqlite3_step(stmt) == SQLITE_ROW)
1087 {
1088 result = (sqlite3_column_type(stmt, 1) != SQLITE_NULL);
1089 }
1090
1091 sqlite3_finalize(stmt);
1092
1093 return result;
1094}
1095
1096GList *dt_ioppr_get_iop_order_list(int32_t imgid, gboolean sorted)
1097{
1098 GList *iop_order_list = NULL;
1099
1100 if(imgid > 0)
1101 {
1102 sqlite3_stmt *stmt;
1103
1104 // we read the iop-order-list in the preset table, the actual version is
1105 // the first int32_t serialized into the io_params. This is then a sequential
1106 // search, but there will not be many such presets and we do call this routine
1107 // only when loading an image and when changing the iop-order.
1108
1109 // clang-format off
1111 "SELECT version, iop_list"
1112 " FROM main.module_order"
1113 " WHERE imgid=?1", -1, &stmt, NULL);
1114 // clang-format on
1115 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
1116
1117 if(sqlite3_step(stmt) == SQLITE_ROW)
1118 {
1119 const dt_iop_order_t version = sqlite3_column_int(stmt, 0);
1120 const gboolean has_iop_list = (sqlite3_column_type(stmt, 1) != SQLITE_NULL);
1121
1122 if(version == DT_IOP_ORDER_CUSTOM || has_iop_list)
1123 {
1124 const char *buf = (char *)sqlite3_column_text(stmt, 1);
1125 if(buf) iop_order_list = dt_ioppr_deserialize_text_iop_order_list(buf);
1126
1127 if(!iop_order_list)
1128 {
1129 // preset not found, fall back to last built-in version, will be loaded below
1130 fprintf(stderr, "[dt_ioppr_get_iop_order_list] error building iop_order_list imgid %d\n", imgid);
1131 }
1132 else
1133 {
1134 // @@_NEW_MODULE: For new module it is required to insert the new module name in the iop-order list here.
1135 // The insertion can be done depending on the current iop-order list kind.
1136 iop_order_list = _insert_before(iop_order_list, "nlmeans", "negadoctor");
1137 iop_order_list = _insert_before(iop_order_list, "negadoctor", "channelmixerrgb");
1138 iop_order_list = _insert_before(iop_order_list, "negadoctor", "censorize");
1139 iop_order_list = _insert_before(iop_order_list, "rgbcurve", "colorbalancergb");
1140 iop_order_list = _insert_before(iop_order_list, "colorbalancergb", "colorprimaries");
1141 iop_order_list = _insert_before(iop_order_list, "colorprimaries", "splittoningrgb");
1142 iop_order_list = _insert_before(iop_order_list, "rgbcurve", "drawlayer");
1143 iop_order_list = _insert_before(iop_order_list, "drawlayer", "colorequal");
1144 iop_order_list = _insert_before(iop_order_list, "ashift", "cacorrectrgb");
1145 iop_order_list = _insert_before(iop_order_list, "graduatednd", "crop");
1146 iop_order_list = _insert_before(iop_order_list, "colorbalance", "diffuse");
1147 iop_order_list = _insert_before(iop_order_list, "nlmeans", "blurs");
1148 iop_order_list = _insert_before(iop_order_list, "ashift", "initialscale");
1149 iop_order_list = _insert_before(iop_order_list, "filmicrgb", "crystgrain");
1150 iop_order_list = _insert_before(iop_order_list, "mask_manager", "detailmask");
1151 iop_order_list = _insert_before(iop_order_list, "rawprepare", "basebuffer");
1152 }
1153 }
1154 else if(version == DT_IOP_ORDER_LEGACY)
1155 {
1156 iop_order_list = _table_to_list(legacy_order);
1157 }
1158 else if(version == DT_IOP_ORDER_V30)
1159 {
1160 iop_order_list = _table_to_list(v30_order);
1161 }
1162 else if(version == DT_IOP_ORDER_V30_JPG)
1163 {
1164 iop_order_list = _table_to_list(v30_jpg_order);
1165 }
1166 else if(version == DT_IOP_ORDER_ANSEL_RAW)
1167 {
1168 iop_order_list = _table_to_list(ansel_raw_order);
1169 }
1170 else if(version == DT_IOP_ORDER_ANSEL_JPG)
1171 {
1172 iop_order_list = _table_to_list(ansel_jpg_order);
1173 }
1174 else
1175 fprintf(stderr, "[dt_ioppr_get_iop_order_list] invalid iop order version %d for imgid %d\n", version, imgid);
1176
1177 if(iop_order_list)
1178 {
1179 _ioppr_reset_iop_order(iop_order_list);
1180 // Perform sanity check after migration to ensure the list is valid
1181 if(!_ioppr_sanity_check_iop_order(iop_order_list))
1182 {
1183 g_list_free_full(iop_order_list, dt_free_gpointer);
1184 iop_order_list = NULL;
1185 fprintf(stderr, "[dt_ioppr_get_iop_order_list] sanity check failed for imgid %d, falling back to default\n", imgid);
1186 }
1187 }
1188 }
1189
1190 sqlite3_finalize(stmt);
1191 }
1192
1193 // Fall back to the image-format default order when no module_order row exists
1194 // yet, for example after deleting history. UNKNOWN_IMAGE keeps the RAW order
1195 // because there is no concrete file format to inspect.
1196 if(!iop_order_list)
1197 {
1198 dt_iop_order_t default_order = DT_IOP_ORDER_ANSEL_RAW;
1199 if(imgid > 0 && !IS_NULL_PTR(darktable.image_cache))
1200 {
1201 const dt_image_t *image = dt_image_cache_testget(darktable.image_cache, imgid, 'r');
1202 if(!IS_NULL_PTR(image))
1203 {
1206 }
1207 }
1208
1209 iop_order_list = dt_ioppr_get_iop_order_list_version(default_order);
1210 }
1211
1212 if(sorted) iop_order_list = g_list_sort(iop_order_list, dt_sort_iop_list_by_order);
1213
1214 return iop_order_list;
1215}
1216
1224static void _ioppr_reset_iop_order(GList *iop_order_list)
1225{
1226 // iop-order must start with a number > 0 and be incremented. There is no
1227 // other constraints.
1228 int iop_order = 1;
1229 for(const GList *l = iop_order_list; l; l = g_list_next(l))
1230 {
1232 e->o.iop_order = iop_order++;
1233 }
1234}
1235
1244{
1245 // make sure that the iop_order_list does not contains possibly removed modules
1246
1247 GList *l = dev->iop_order_list;
1248 while(l)
1249 {
1250 GList *next = g_list_next(l); // need to get next pointer now, as we may be deleting this node
1251 const dt_iop_order_entry_t *const restrict e = (dt_iop_order_entry_t *)l->data;
1252 const dt_iop_module_t *const restrict mod = dt_iop_get_module_by_op_priority(dev->iop, e->operation, e->instance);
1253 if(IS_NULL_PTR(mod))
1254 {
1255 dt_free(l->data);
1256 dev->iop_order_list = g_list_delete_link(dev->iop_order_list, l);
1257 }
1258
1259 l = next;
1260 }
1261}
1262
1263void dt_ioppr_resync_pipeline(dt_develop_t *dev, const int32_t imgid, const char *msg, gboolean check_duplicates)
1264{
1267 if(check_duplicates) dt_ioppr_check_duplicate_iop_order(&dev->iop, dev->history);
1268 if(msg) dt_ioppr_check_iop_order(dev, imgid, msg);
1269}
1270
1272{
1274
1275 // and reset all module iop_order
1276
1277 GList *modules = dev->iop;
1278 while(modules)
1279 {
1280 dt_iop_module_t *mod = (dt_iop_module_t *)(modules->data);
1281 GList *next = g_list_next(modules);
1282
1283 // modules with iop_order set to INT_MAX we keep them as they will be removed (non visible)
1284 // _update_iop_visibility.
1285 if(mod->iop_order != INT_MAX)
1287
1288 modules = next;
1289 }
1290
1291 dev->iop = g_list_sort(dev->iop, dt_sort_iop_by_order);
1292}
1293
1294void dt_ioppr_rebuild_iop_order_from_modules(struct dt_develop_t *dev, GList *ordered_modules)
1295{
1296 if(IS_NULL_PTR(dev) || !ordered_modules) return;
1297
1298 GList *new_iop_order_list = NULL;
1299 int order = 1;
1300
1301 for(const GList *l = ordered_modules; l; l = g_list_next(l))
1302 {
1303 const dt_iop_module_t *mod = (const dt_iop_module_t *)l->data;
1304 if(IS_NULL_PTR(mod)) continue;
1305
1306 dt_iop_order_entry_t *entry = (dt_iop_order_entry_t *)calloc(1, sizeof(dt_iop_order_entry_t));
1307 g_strlcpy(entry->operation, mod->op, sizeof(entry->operation));
1308 entry->instance = mod->multi_priority;
1309 g_strlcpy(entry->name, mod->multi_name, sizeof(entry->name));
1310 entry->o.iop_order = order++;
1311 new_iop_order_list = g_list_append(new_iop_order_list, entry);
1312 }
1313
1314 if(new_iop_order_list)
1315 {
1316 g_list_free_full(dev->iop_order_list, dt_free_gpointer);
1317 dev->iop_order_list = new_iop_order_list;
1319 }
1320}
1321
1322// sets the iop_order on each module of *_iop_list
1323// iop_order is set only for base modules, multi-instances will be flagged as unused with INT_MAX
1324// if a module do not exists on iop_order_list it is flagged as unused with INT_MAX
1325void dt_ioppr_set_default_iop_order(dt_develop_t *dev, const int32_t imgid)
1326{
1327 // First check whether the image already owns an order in DB. If it does not,
1328 // choose the built-in order from dev->image_storage, which was just refreshed
1329 // by the darkroom/history caller and is the image state used by module reloads.
1330 gboolean has_stored_order = FALSE;
1331 if(imgid > 0)
1332 {
1333 sqlite3_stmt *stmt;
1335 "SELECT 1 FROM main.module_order WHERE imgid = ?1", -1,
1336 &stmt, NULL);
1337 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
1338 has_stored_order = (sqlite3_step(stmt) == SQLITE_ROW);
1339 sqlite3_finalize(stmt);
1340 }
1341
1342 GList *iop_order_list = NULL;
1343 if(!has_stored_order && dev->image_storage.id == imgid)
1344 {
1345 const dt_iop_order_t default_order = dt_image_needs_rawprepare(&dev->image_storage)
1348 iop_order_list = dt_ioppr_get_iop_order_list_version(default_order);
1349 }
1350 else
1351 {
1352 iop_order_list = dt_ioppr_get_iop_order_list(imgid, FALSE);
1353 }
1354
1355 // we assign a single iop-order to each module
1356
1357 _ioppr_reset_iop_order(iop_order_list);
1358
1359 if(dev->iop_order_list)
1360 {
1361 g_list_free_full(dev->iop_order_list, dt_free_gpointer);
1362 dev->iop_order_list = NULL;
1363 }
1364 dev->iop_order_list = iop_order_list;
1365
1366 // we now set the module list given to this iop-order
1367
1369}
1370
1377static void dt_ioppr_migrate_iop_order(struct dt_develop_t *dev, const int32_t imgid)
1378{
1383 dt_dev_history_notify_change(dev, imgid);
1384}
1385
1386void dt_ioppr_change_iop_order(struct dt_develop_t *dev, const int32_t imgid, GList *new_iop_list)
1387{
1388 GList *iop_list = dt_ioppr_iop_order_copy_deep(new_iop_list);
1390
1391 if(mi) iop_list = dt_ioppr_merge_multi_instance_iop_order_list(iop_list, mi);
1392
1395 g_list_free_full(iop_list, dt_free_gpointer);
1396 iop_list = NULL;
1397
1399}
1400
1407static GList *dt_ioppr_extract_multi_instances_list(GList *iop_order_list)
1408{
1409 GList *mi = NULL;
1410
1411 for(const GList *l = iop_order_list; l; l = g_list_next(l))
1412 {
1413 const dt_iop_order_entry_t *const restrict entry = (dt_iop_order_entry_t *)l->data;
1414
1415 if(_count_entries_operation(iop_order_list, entry->operation) > 1)
1416 {
1418 mi = g_list_prepend(mi, copy);
1419 }
1420 }
1421
1422 return g_list_reverse(mi); // list was built in reverse order, so un-reverse it
1423}
1424
1436 const char *operation, GList *multi_instance_list)
1437{
1438 const int count_to = _count_entries_operation(iop_order_list, operation);
1439
1440 int item_nb = 0;
1441
1442 GList *link = iop_order_list;
1443
1444 for(const GList *l = multi_instance_list; l; l = g_list_next(l))
1445 {
1446 dt_iop_order_entry_t *entry = (dt_iop_order_entry_t *)l->data;
1447
1448 item_nb++;
1449
1450 if(item_nb <= count_to)
1451 {
1452 link = dt_ioppr_get_iop_order_link(link, operation, -1);
1453 dt_iop_order_entry_t *e = (dt_iop_order_entry_t *)link->data;
1454 e->instance = entry->instance;
1455
1456 // free this entry as not merged into the list
1457 dt_free(entry);
1458
1459 // next replace should happen to any module after this one
1460 link = g_list_next(link);
1461 }
1462 else
1463 {
1464 iop_order_list = g_list_insert_before(iop_order_list, link, entry);
1465 }
1466 }
1467
1468 // if needed removes all other instance of this operation which are superfluous
1469 if(g_list_shorter_than(multi_instance_list, count_to))
1470 {
1471 while(link)
1472 {
1473 const dt_iop_order_entry_t *const restrict entry = (dt_iop_order_entry_t *)link->data;
1474 GList *next = g_list_next(link);
1475 if(strcmp(operation, entry->operation) == 0)
1476 {
1477 dt_free(link->data);
1478 iop_order_list = g_list_delete_link(iop_order_list, link);
1479 }
1480
1481 link = next;
1482 }
1483 }
1484
1485 return iop_order_list;
1486}
1487
1498static GList *dt_ioppr_merge_multi_instance_iop_order_list(GList *iop_order_list, GList *multi_instance_list)
1499{
1500 GList *op = NULL;
1501
1502 GList *copy = dt_ioppr_iop_order_copy_deep(multi_instance_list);
1503 GList *l = copy;
1504
1505 while(l)
1506 {
1507 dt_iop_order_entry_t *entry = (dt_iop_order_entry_t *)l->data;
1508 GList *l_next = g_list_next(l);
1509
1510 op = g_list_append(op, entry);
1511
1512 copy = g_list_delete_link(copy, l);
1513
1514 GList *mi = l_next;
1515 while(mi)
1516 {
1517 GList *next = g_list_next(mi);
1518 dt_iop_order_entry_t *mi_entry = (dt_iop_order_entry_t *)mi->data;
1519 if(strcmp(entry->operation, mi_entry->operation) == 0)
1520 {
1521 op = g_list_append(op, mi_entry);
1522 copy = g_list_delete_link(copy, mi);
1523 }
1524
1525 mi = next;
1526 }
1527
1528 // copy operation as entry may be freed
1529 char operation[20];
1530 memcpy(operation, entry->operation, sizeof(entry->operation));
1531
1532 iop_order_list = dt_ioppr_merge_module_multi_instance_iop_order_list(iop_order_list, operation, op);
1533
1534 g_list_free(op);
1535 op = NULL;
1536
1537 l = copy;
1538 }
1539
1540 return iop_order_list;
1541}
1542
1553static void _count_iop_module(GList *iop, const char *operation, int *max_multi_priority, int *count,
1554 int *max_multi_priority_enabled, int *count_enabled)
1555{
1556 *max_multi_priority = 0;
1557 *count = 0;
1558 *max_multi_priority_enabled = 0;
1559 *count_enabled = 0;
1560
1561 for(const GList *modules = iop; modules; modules = g_list_next(modules))
1562 {
1563 const dt_iop_module_t *const restrict mod = (dt_iop_module_t *)modules->data;
1564 if(!strcmp(mod->op, operation))
1565 {
1566 (*count)++;
1567 if(*max_multi_priority < mod->multi_priority) *max_multi_priority = mod->multi_priority;
1568
1569 if(mod->enabled)
1570 {
1571 (*count_enabled)++;
1572 if(*max_multi_priority_enabled < mod->multi_priority) *max_multi_priority_enabled = mod->multi_priority;
1573 }
1574 }
1575 }
1576
1577 assert(*count >= *count_enabled);
1578}
1579
1587static int _count_entries_operation(GList *e_list, const char *operation)
1588{
1589 int count = 0;
1590
1591 for(const GList *l = e_list; l; l = g_list_next(l))
1592 {
1594 if(!strcmp(ep->operation, operation)) count++;
1595 }
1596
1597 return count;
1598}
1599
1607static gboolean _operation_already_handled(GList *e_list, const char *operation)
1608{
1609 for(const GList *l = g_list_previous(e_list); l; l = g_list_previous(l))
1610 {
1611 const dt_iop_order_entry_t *const restrict ep = (dt_iop_order_entry_t *)l->data;
1612 if(!strcmp(ep->operation, operation)) return TRUE;
1613 }
1614 return FALSE;
1615}
1616
1626int _get_multi_priority(dt_develop_t *dev, const char *operation, const int n, const gboolean only_disabled)
1627{
1628 int count = 0;
1629 for(const GList *l = dev->iop; l; l = g_list_next(l))
1630 {
1631 const dt_iop_module_t *const restrict mod = (dt_iop_module_t *)l->data;
1632 if((!only_disabled || mod->enabled == FALSE) && !strcmp(mod->op, operation))
1633 {
1634 count++;
1635 if(count == n) return mod->multi_priority;
1636 }
1637 }
1638
1639 return INT_MAX;
1640}
1641
1651static void dt_ioppr_update_for_entries(dt_develop_t *dev, GList *entry_list, gboolean append)
1652{
1653
1654 // for each priority list to be checked
1655 for(GList *e_list = entry_list; e_list; e_list = g_list_next(e_list))
1656 {
1657 const dt_iop_order_entry_t *const restrict ep = (dt_iop_order_entry_t *)e_list->data;
1658
1659 gboolean force_append = FALSE;
1660
1661 // we also need to force append (even if overwrite mode is
1662 // selected - append = FALSE) when a module has a specific name
1663 // and this name is not present into the current iop list.
1664
1665 if(*ep->name && !dt_iop_get_module_by_instance_name(dev->iop, ep->operation, ep->name))
1666 force_append = TRUE;
1667
1668 int max_multi_priority = 0, count = 0;
1669 int max_multi_priority_enabled = 0, count_enabled = 0;
1670
1671 // is it a currently active module and if so how many active instances we have
1672 _count_iop_module(dev->iop, ep->operation,
1673 &max_multi_priority, &count, &max_multi_priority_enabled, &count_enabled);
1674
1675 // look for this operation into the target iop-order list and add there as much operation as needed
1676
1677 for(GList *l = g_list_last(dev->iop_order_list); l; l = g_list_previous(l))
1678 {
1679 const dt_iop_order_entry_t *const restrict e = (dt_iop_order_entry_t *)l->data;
1680 if(!strcmp(e->operation, ep->operation) && !_operation_already_handled(e_list, ep->operation))
1681 {
1682 // how many instances of this module in the entry list, and re-number multi-priority accordingly
1683 const int new_active_instances = _count_entries_operation(entry_list, ep->operation);
1684
1685 int add_count = 0;
1686 int start_multi_priority = 0;
1687 int nb_replace = 0;
1688
1689 if(append || force_append)
1690 {
1691 nb_replace = count - count_enabled;
1692 add_count = MAX(0, new_active_instances - nb_replace);
1693 start_multi_priority = max_multi_priority + 1;
1694 }
1695 else
1696 {
1697 nb_replace = count;
1698 add_count = MAX(0, new_active_instances - count);
1699 start_multi_priority = max_multi_priority + 1;
1700 }
1701
1702 // update multi_priority to be unique in iop list
1703 int multi_priority = start_multi_priority;
1704 int nb = 0;
1705
1706 for(const GList *s = entry_list; s; s = g_list_next(s))
1707 {
1708 dt_iop_order_entry_t *item = (dt_iop_order_entry_t *)s->data;
1709 if(!strcmp(item->operation, e->operation))
1710 {
1711 nb++;
1712 if(nb <= nb_replace)
1713 {
1714 // this one replaces current module, get it's multi-priority
1715 item->instance = _get_multi_priority(dev, item->operation, nb, append);
1716 }
1717 else
1718 {
1719 // otherwise create a new multi-priority
1720 item->instance = multi_priority++;
1721 }
1722 }
1723 }
1724
1725 multi_priority = start_multi_priority;
1726
1727 l = g_list_next(l);
1728
1729 for(int k = 0; k<add_count; k++)
1730 {
1732 g_strlcpy(n->operation, ep->operation, sizeof(n->operation));
1733 n->instance = multi_priority++;
1734 n->o.iop_order = 0;
1735 dev->iop_order_list = g_list_insert_before(dev->iop_order_list, l, n);
1736 }
1737 break;
1738 }
1739 }
1740 }
1741
1743
1744// dt_ioppr_print_iop_order(dev->iop_order_list, "upd sitem");
1745}
1746
1747void dt_ioppr_update_for_style_items(dt_develop_t *dev, GList *st_items, gboolean append)
1748{
1749 GList *e_list = NULL;
1750
1751 // for each priority list to be checked
1752 for(const GList *si_list = st_items; si_list; si_list = g_list_next(si_list))
1753 {
1754 const dt_style_item_t *const restrict si = (dt_style_item_t *)si_list->data;
1755
1757 memcpy(n->operation, si->operation, sizeof(n->operation));
1758 n->instance = si->multi_priority;
1759 g_strlcpy(n->name, si->multi_name, sizeof(n->name));
1760 n->o.iop_order = 0;
1761 e_list = g_list_prepend(e_list, n);
1762 }
1763 e_list = g_list_reverse(e_list); // list was built in reverse order, so un-reverse it
1764
1765 dt_ioppr_update_for_entries(dev, e_list, append);
1766
1767 // write back the multi-priority
1768
1769 GList *el = e_list;
1770 for(const GList *si_list = st_items; si_list; si_list = g_list_next(si_list))
1771 {
1772 dt_style_item_t *si = (dt_style_item_t *)si_list->data;
1773 const dt_iop_order_entry_t *const restrict e = (dt_iop_order_entry_t *)el->data;
1774
1775 si->multi_priority = e->instance;
1777 el = g_list_next(el);
1778 }
1779
1780 g_list_free(e_list);
1781 e_list = NULL;
1782}
1783
1784void dt_ioppr_update_for_modules(dt_develop_t *dev, GList *modules, gboolean append)
1785{
1786 GList *e_list = NULL;
1787
1788 // for each priority list to be checked
1789 for(const GList *m_list = modules; m_list; m_list = g_list_next(m_list))
1790 {
1791 const dt_iop_module_t *const restrict mod = (dt_iop_module_t *)m_list->data;
1792
1794 g_strlcpy(n->operation, mod->op, sizeof(n->operation));
1795 n->instance = mod->multi_priority;
1796 g_strlcpy(n->name, mod->multi_name, sizeof(n->name));
1797 n->o.iop_order = 0;
1798 e_list = g_list_prepend(e_list, n);
1799 }
1800 e_list = g_list_reverse(e_list); // list was built in reverse order, so un-reverse it
1801
1802 dt_ioppr_update_for_entries(dev, e_list, append);
1803
1804 // write back the multi-priority
1805
1806 GList *el = e_list;
1807 for(const GList *m_list = modules; m_list; m_list = g_list_next(m_list))
1808 {
1809 dt_iop_module_t *mod = (dt_iop_module_t *)m_list->data;
1811
1812 mod->multi_priority = e->instance;
1814
1815 el = g_list_next(el);
1816 }
1817
1818 g_list_free_full(e_list, dt_free_gpointer);
1819 e_list = NULL;
1820}
1821
1822// returns the first dt_dev_history_item_t on history_list where hist->module == mod
1831{
1832 dt_dev_history_item_t *hist_entry = NULL;
1833
1834 for(const GList *history = history_list; history; history = g_list_next(history))
1835 {
1836 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
1837
1838 if(hist->module == mod)
1839 {
1840 hist_entry = hist;
1841 break;
1842 }
1843 }
1844
1845 return hist_entry;
1846}
1847
1848// check if there's duplicate iop_order entries in iop_list
1849// if so, updates the iop_order to be unique, but only if the module is disabled and not in history
1859static void dt_ioppr_check_duplicate_iop_order(GList **_iop_list, GList *history_list)
1860{
1861 GList *iop_list = *_iop_list;
1862 dt_iop_module_t *mod_prev = NULL;
1863
1864 // get the first module
1865 GList *modules = iop_list;
1866 if(modules)
1867 {
1868 mod_prev = (dt_iop_module_t *)(modules->data);
1869 modules = g_list_next(modules);
1870 }
1871 // check for each module if iop_order is the same as the previous one
1872 // if so, change it, but only if disabled and not in history
1873 while(modules)
1874 {
1875 int reset_list = 0;
1876 dt_iop_module_t *mod = (dt_iop_module_t *)(modules->data);
1877
1878 if(mod->iop_order == mod_prev->iop_order && mod->iop_order != INT_MAX)
1879 {
1880 int can_move = 0;
1881
1882 if(!mod->enabled && _ioppr_search_history_by_module(history_list, mod) == NULL)
1883 {
1884 can_move = 1;
1885
1886 GList *modules1 = g_list_next(modules);
1887 if(modules1)
1888 {
1889 dt_iop_module_t *mod_next = (dt_iop_module_t *)(modules1->data);
1890 if(mod->iop_order != mod_next->iop_order)
1891 {
1892 mod->iop_order += (mod_next->iop_order - mod->iop_order) / 2.0;
1893 }
1894 else
1895 {
1896 dt_ioppr_check_duplicate_iop_order(&modules, history_list);
1897 reset_list = 1;
1898 }
1899 }
1900 else
1901 {
1902 mod->iop_order += 1.0;
1903 }
1904 }
1905 else if(!mod_prev->enabled && _ioppr_search_history_by_module(history_list, mod_prev) == NULL)
1906 {
1907 can_move = 1;
1908
1909 GList *modules1 = g_list_previous(modules);
1910 if(modules1) modules1 = g_list_previous(modules1);
1911 if(modules1)
1912 {
1913 dt_iop_module_t *mod_next = (dt_iop_module_t *)(modules1->data);
1914 if(mod_prev->iop_order != mod_next->iop_order)
1915 {
1916 mod_prev->iop_order -= (mod_prev->iop_order - mod_next->iop_order) / 2.0;
1917 }
1918 else
1919 {
1920 can_move = 0;
1921 fprintf(stderr,
1922 "[dt_ioppr_check_duplicate_iop_order 1] modules %s %s(%d) and %s %s(%d) have the same iop_order\n",
1923 mod_prev->op, mod_prev->multi_name, mod_prev->iop_order, mod->op, mod->multi_name, mod->iop_order);
1924 }
1925 }
1926 else
1927 {
1928 mod_prev->iop_order -= 0.5;
1929 }
1930 }
1931
1932 if(!can_move)
1933 {
1934 fprintf(stderr,
1935 "[dt_ioppr_check_duplicate_iop_order] modules %s %s(%d) and %s %s(%d) have the same iop_order\n",
1936 mod_prev->op, mod_prev->multi_name, mod_prev->iop_order, mod->op, mod->multi_name, mod->iop_order);
1937 }
1938 }
1939
1940 if(reset_list)
1941 {
1942 modules = iop_list;
1943 if(modules)
1944 {
1945 mod_prev = (dt_iop_module_t *)(modules->data);
1946 modules = g_list_next(modules);
1947 }
1948 }
1949 else
1950 {
1951 mod_prev = mod;
1952 modules = g_list_next(modules);
1953 }
1954 }
1955
1956 *_iop_list = iop_list;
1957}
1958
1959// check if all so modules on iop_list have a iop_order defined in iop_order_list
1960int dt_ioppr_check_so_iop_order(GList *iop_list, GList *iop_order_list)
1961{
1962 int iop_order_missing = 0;
1963
1964 // check if all the modules have their iop_order assigned
1965 for(const GList *modules = iop_list; modules; modules = g_list_next(modules))
1966 {
1967 const dt_iop_module_so_t *const restrict mod = (dt_iop_module_so_t *)(modules->data);
1968 const dt_iop_order_entry_t *const restrict entry =
1969 dt_ioppr_get_iop_order_entry(iop_order_list, mod->op, 0); // mod->multi_priority);
1970 if(IS_NULL_PTR(entry) && !dt_deprecated(mod->op))
1971 {
1972 iop_order_missing = 1;
1973 fprintf(stderr, "[dt_ioppr_check_so_iop_order] missing iop_order for module %s\n", mod->op);
1974 }
1975 }
1976
1977 return iop_order_missing;
1978}
1979
1987static void *_dup_iop_order_entry(const void *src, gpointer data)
1988{
1989 const dt_iop_order_entry_t *const restrict scr_entry = (dt_iop_order_entry_t *)src;
1990 dt_iop_order_entry_t *new_entry = malloc(sizeof(dt_iop_order_entry_t));
1991 memcpy(new_entry, scr_entry, sizeof(dt_iop_order_entry_t));
1992 return (void *)new_entry;
1993}
1994
1995// returns a duplicate of iop_order_list
1996GList *dt_ioppr_iop_order_copy_deep(GList *iop_order_list)
1997{
1998 return (GList *)g_list_copy_deep(iop_order_list, _dup_iop_order_entry, NULL);
1999}
2000
2001// helper to sort a GList of dt_iop_module_t by iop_order
2002gint dt_sort_iop_by_order(gconstpointer a, gconstpointer b)
2003{
2004 const dt_iop_module_t *const restrict am = (const dt_iop_module_t *)a;
2005 const dt_iop_module_t *const restrict bm = (const dt_iop_module_t *)b;
2006 if(am->iop_order > bm->iop_order) return 1;
2007 if(am->iop_order < bm->iop_order) return -1;
2008 return 0;
2009}
2010
2011// if module can be placed before than module_next on the pipe
2012// it returns the new iop_order
2013// if it cannot be placed it returns -1.0
2014// this assumes that the order is always positive
2015gboolean dt_ioppr_check_can_move_before_iop(GList *iop_list, dt_iop_module_t *module, dt_iop_module_t *module_next)
2016{
2017 // we should't be here if the next module is using a raster mask and and our module is that raster mask source
2018 if(module_next->raster_mask.sink.source == module)
2019 return FALSE;
2020
2021 gboolean can_move = FALSE;
2022
2023 // module is before on the pipe
2024 // move it up
2025 if(module->iop_order < module_next->iop_order)
2026 {
2027 // let's first search for module
2028 GList *modules = iop_list;
2029 for(; modules; modules = g_list_next(modules))
2030 {
2031 const dt_iop_module_t *const restrict mod = (dt_iop_module_t *)modules->data;
2032 if(mod == module) break;
2033 }
2034
2035 // we found the module
2036 if(modules)
2037 {
2038 dt_iop_module_t *mod1 = NULL;
2039 dt_iop_module_t *mod2 = NULL;
2040
2041 // now search for module_next and the one previous to that, so iop_order can be calculated
2042 // also check the rules
2043 for(modules = g_list_next(modules); modules; modules = g_list_next(modules))
2044 {
2045 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
2046
2047 // if we reach module_next everything is OK
2048 if(mod == module_next)
2049 {
2050 mod2 = mod;
2051 break;
2052 }
2053
2054 // moving a module that is the source for a raster mask ABOVE the module using it is forbidden
2055 if(mod->raster_mask.sink.source == module)
2056 break;
2057
2058 // is there a rule about swapping this two?
2059 int rule_found = 0;
2060 for(const GList *rules = darktable.iop_order_rules; rules; rules = g_list_next(rules))
2061 {
2062 const dt_iop_order_rule_t *const restrict rule = (dt_iop_order_rule_t *)rules->data;
2063
2064 if(strcmp(module->op, rule->op_prev) == 0 && strcmp(mod->op, rule->op_next) == 0)
2065 {
2066 rule_found = 1;
2067 break;
2068 }
2069 }
2070 if(rule_found) break;
2071
2072 mod1 = mod;
2073 }
2074
2075 // we reach the module_next module
2076 if(mod2)
2077 {
2078 // this is already the previous module!
2079 if(module == mod1)
2080 {
2081 ;
2082 }
2083 else if(mod1->iop_order == mod2->iop_order)
2084 {
2085 fprintf(stderr, "[dt_ioppr_get_iop_order_before_iop] %s %s(%d) and %s %s(%d) have the same iop_order\n",
2086 mod1->op, mod1->multi_name, mod1->iop_order, mod2->op, mod2->multi_name, mod2->iop_order);
2087 }
2088 else
2089 {
2090 can_move = TRUE;
2091 }
2092 }
2093 }
2094 else
2095 fprintf(stderr, "[dt_ioppr_get_iop_order_before_iop] can't find module %s %s\n", module->op, module->multi_name);
2096 }
2097 // module is next on the pipe
2098 // move it down
2099 else if(module->iop_order > module_next->iop_order)
2100 {
2101 // let's first search for module
2102 GList *modules = g_list_last(iop_list);
2103 for(; modules; modules = g_list_previous(modules))
2104 {
2105 const dt_iop_module_t *const restrict mod = (dt_iop_module_t *)modules->data;
2106 if(mod == module) break;
2107 }
2108
2109 // we found the module
2110 if(modules)
2111 {
2112 dt_iop_module_t *mod1 = NULL;
2113 dt_iop_module_t *mod2 = NULL;
2114
2115 // now search for module_next and the one next to that, so iop_order can be calculated
2116 // also check the rules
2117 for(modules = g_list_previous(modules); modules; modules = g_list_previous(modules))
2118 {
2119 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
2120
2121 // we reach the module next to module_next, everything is OK
2122 if(!IS_NULL_PTR(mod2))
2123 {
2124 mod1 = mod;
2125 break;
2126 }
2127
2128 // moving a module using a raster mask BELOW its raster source module is forbidden
2129 if(module->raster_mask.sink.source == mod)
2130 break;
2131
2132 // is there a rule about swapping this two?
2133 int rule_found = 0;
2134 for(const GList *rules = darktable.iop_order_rules; rules; rules = g_list_next(rules))
2135 {
2136 const dt_iop_order_rule_t *const restrict rule = (dt_iop_order_rule_t *)rules->data;
2137
2138 if(strcmp(mod->op, rule->op_prev) == 0 && strcmp(module->op, rule->op_next) == 0)
2139 {
2140 rule_found = 1;
2141 break;
2142 }
2143 }
2144 if(rule_found) break;
2145
2146 if(mod == module_next) mod2 = mod;
2147 }
2148
2149 // we reach the module_next module
2150 if(mod1)
2151 {
2152 // this is already the previous module!
2153 if(module == mod2)
2154 {
2155 ;
2156 }
2157 else if(mod1->iop_order == mod2->iop_order)
2158 {
2159 fprintf(stderr, "[dt_ioppr_get_iop_order_before_iop] %s %s(%d) and %s %s(%d) have the same iop_order\n",
2160 mod1->op, mod1->multi_name, mod1->iop_order, mod2->op, mod2->multi_name, mod2->iop_order);
2161 }
2162 else
2163 {
2164 can_move = TRUE;
2165 }
2166 }
2167 }
2168 else
2169 fprintf(stderr, "[dt_ioppr_get_iop_order_before_iop] can't find module %s %s\n", module->op, module->multi_name);
2170 }
2171 else
2172 {
2173 fprintf(stderr, "[dt_ioppr_get_iop_order_before_iop] modules %s %s(%d) and %s %s(%d) have the same iop_order\n",
2174 module->op, module->multi_name, module->iop_order, module_next->op, module_next->multi_name, module_next->iop_order);
2175 }
2176
2177 return can_move;
2178}
2179
2180// if module can be placed after than module_prev on the pipe
2181// it returns the new iop_order
2182// if it cannot be placed it returns -1.0
2183// this assumes that the order is always positive
2184gboolean dt_ioppr_check_can_move_after_iop(GList *iop_list, dt_iop_module_t *module, dt_iop_module_t *module_prev)
2185{
2186 // we shouldn't be here if the previous module is the a raster mask's source and our module is using it
2187 if(module->raster_mask.sink.source == module_prev)
2188 return FALSE;
2189
2190 gboolean can_move = FALSE;
2191
2192 // moving after module_prev is the same as moving before the very next one after module_prev
2193 dt_iop_module_t *module_next = NULL;
2194
2195 for(const GList *modules = g_list_last(iop_list); modules; modules = g_list_previous(modules))
2196 {
2197 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
2198 if(mod == module_prev) break;
2199
2200 module_next = mod;
2201 }
2202 if(IS_NULL_PTR(module_next))
2203 {
2204 fprintf(
2205 stderr,
2206 "[dt_ioppr_get_iop_order_after_iop] can't find module previous to %s %s(%d) while moving %s %s(%d) after it\n",
2207 module_prev->op, module_prev->multi_name, module_prev->iop_order, module->op, module->multi_name,
2208 module->iop_order);
2209 }
2210 else
2211 can_move = dt_ioppr_check_can_move_before_iop(iop_list, module, module_next);
2212
2213 return can_move;
2214}
2215
2216// changes the module->iop_order so it comes before in the pipe than module_next
2217// sort dev->iop to reflect the changes
2218// return 1 if iop_order is changed, 0 otherwise
2219gboolean dt_ioppr_move_iop_before(struct dt_develop_t *dev, dt_iop_module_t *module, dt_iop_module_t *module_next)
2220{
2221 GList *next = dt_ioppr_get_iop_order_link(dev->iop_order_list, module_next->op, module_next->multi_priority);
2222 GList *current = dt_ioppr_get_iop_order_link(dev->iop_order_list, module->op, module->multi_priority);
2223
2224 if(IS_NULL_PTR(next)) return FALSE;
2225
2226 if(IS_NULL_PTR(current))
2227 {
2228 // Module not in iop_order_list, create entry
2230 g_strlcpy(entry->operation, module->op, sizeof(entry->operation));
2231 entry->instance = module->multi_priority;
2232 entry->o.iop_order = 0; // will be reset later
2233 current = g_list_alloc();
2234 current->data = entry;
2235 }
2236
2237 dev->iop_order_list = g_list_remove_link(dev->iop_order_list, current);
2238 dev->iop_order_list = g_list_insert_before(dev->iop_order_list, next, current->data);
2239
2240 g_list_free(current);
2241 current = NULL;
2242
2244
2245 return TRUE;
2246}
2247
2248// changes the module->iop_order so it comes after in the pipe than module_prev
2249// sort dev->iop to reflect the changes
2250// return 1 if iop_order is changed, 0 otherwise
2251gboolean dt_ioppr_move_iop_after(struct dt_develop_t *dev, dt_iop_module_t *module, dt_iop_module_t *module_prev)
2252{
2253 GList *prev = dt_ioppr_get_iop_order_link(dev->iop_order_list, module_prev->op, module_prev->multi_priority);
2254 GList *current = dt_ioppr_get_iop_order_link(dev->iop_order_list, module->op, module->multi_priority);
2255
2256 if(IS_NULL_PTR(prev)) return FALSE;
2257
2258 if(IS_NULL_PTR(current))
2259 {
2260 // Module not in iop_order_list, create entry
2262 g_strlcpy(entry->operation, module->op, sizeof(entry->operation));
2263 entry->instance = module->multi_priority;
2264 entry->o.iop_order = 0; // will be reset later
2265 current = g_list_alloc();
2266 current->data = entry;
2267 }
2268
2269 dev->iop_order_list = g_list_remove_link(dev->iop_order_list, current);
2270
2271 // we want insert after => so insert before the next item
2272 GList *next = g_list_next(prev);
2273 if(prev)
2274 dev->iop_order_list = g_list_insert_before(dev->iop_order_list, next, current->data);
2275 else
2276 dev->iop_order_list = g_list_append(dev->iop_order_list, current->data);
2277
2278 g_list_free(current);
2279 current = NULL;
2280
2282
2283 return TRUE;
2284}
2285
2295static void _ioppr_check_rules(GList *iop_list, const int32_t imgid, const char *msg)
2296{
2297 // for each module check if it doesn't break a rule
2298 for(const GList *modules = iop_list; modules; modules = g_list_next(modules))
2299 {
2300 const dt_iop_module_t *const restrict mod = (dt_iop_module_t *)modules->data;
2301 if(mod->iop_order == INT_MAX)
2302 {
2303 continue;
2304 }
2305
2306 // we have a module, now check each rule
2307 for(const GList *rules = darktable.iop_order_rules; rules; rules = g_list_next(rules))
2308 {
2309 const dt_iop_order_rule_t *const restrict rule = (dt_iop_order_rule_t *)rules->data;
2310
2311 // mod must be before rule->op_next
2312 if(strcmp(mod->op, rule->op_prev) == 0)
2313 {
2314 // check if there's a rule->op_next module before mod
2315 for(const GList *modules_prev = g_list_previous(modules);
2316 modules_prev;
2317 modules_prev = g_list_previous(modules_prev))
2318 {
2319 const dt_iop_module_t *const restrict mod_prev = (dt_iop_module_t *)modules_prev->data;
2320
2321 if(strcmp(mod_prev->op, rule->op_next) == 0)
2322 {
2323 fprintf(stderr, "[_ioppr_check_rules] found rule %s %s module %s %s(%d) is after %s %s(%d) image %i (%s)\n",
2324 rule->op_prev, rule->op_next, mod->op, mod->multi_name, mod->iop_order, mod_prev->op,
2325 mod_prev->multi_name, mod_prev->iop_order, imgid, msg);
2326 }
2327 }
2328 }
2329 // mod must be after rule->op_prev
2330 else if(strcmp(mod->op, rule->op_next) == 0)
2331 {
2332 // check if there's a rule->op_prev module after mod
2333 for(const GList *modules_next = g_list_next(modules); modules_next; modules_next = g_list_next(modules_next))
2334 {
2335 const dt_iop_module_t *const restrict mod_next = (dt_iop_module_t *)modules_next->data;
2336
2337 if(strcmp(mod_next->op, rule->op_prev) == 0)
2338 {
2339 fprintf(stderr, "[_ioppr_check_rules] found rule %s %s module %s %s(%d) is before %s %s(%d) image %i (%s)\n",
2340 rule->op_prev, rule->op_next, mod->op, mod->multi_name, mod->iop_order, mod_next->op,
2341 mod_next->multi_name, mod_next->iop_order, imgid, msg);
2342 }
2343 }
2344 }
2345 }
2346 }
2347}
2348
2350{
2351 const char *operation = module->op;
2352 const int32_t instance = module->multi_priority;
2353
2355
2356 g_strlcpy(entry->operation, operation, sizeof(entry->operation));
2357 entry->instance = instance;
2358 entry->o.iop_order = 0;
2359
2360 GList *place = NULL;
2361
2362 int max_instance = -1;
2363
2364 for(GList *l = dev->iop_order_list; l; l = g_list_next(l))
2365 {
2366 const dt_iop_order_entry_t *const restrict e = (dt_iop_order_entry_t *)l->data;
2367 if(!strcmp(e->operation, operation) && e->instance > max_instance)
2368 {
2369 place = l;
2370 max_instance = e->instance;
2371 }
2372 }
2373
2374 dev->iop_order_list = g_list_insert_before(dev->iop_order_list, place, entry);
2375}
2376
2377int dt_ioppr_check_iop_order(dt_develop_t *dev, const int32_t imgid, const char *msg)
2378{
2379 int iop_order_ok = 1;
2380
2381 // check if gamma is the last iop
2382 {
2383 GList *modules;
2384 for(modules = g_list_last(dev->iop); modules; modules = g_list_previous(dev->iop))
2385 {
2386 const dt_iop_module_t *const restrict mod = (dt_iop_module_t *)modules->data;
2387 if(mod->iop_order != INT_MAX)
2388 break;
2389 }
2390 if(modules)
2391 {
2392 const dt_iop_module_t *const restrict mod = (dt_iop_module_t *)modules->data;
2393
2394 if(strcmp(mod->op, "gamma") != 0)
2395 {
2396 iop_order_ok = 0;
2397 fprintf(stderr, "[dt_ioppr_check_iop_order] gamma is not the last iop, last is %s %s(%d) image %i (%s)\n",
2398 mod->op, mod->multi_name, mod->iop_order,imgid, msg);
2399 }
2400 }
2401 else
2402 {
2403 // fprintf(stderr, "[dt_ioppr_check_iop_order] dev->iop is empty image %i (%s)\n",imgid, msg);
2404 }
2405 }
2406
2407 // some other checks
2408 {
2409 for(const GList *modules = g_list_last(dev->iop); modules; modules = g_list_previous(dev->iop))
2410 {
2411 const dt_iop_module_t *const restrict mod = (dt_iop_module_t *)modules->data;
2412 if(!mod->default_enabled && mod->iop_order != INT_MAX)
2413 {
2414 if(mod->enabled)
2415 {
2416 iop_order_ok = 0;
2417 fprintf(stderr, "[dt_ioppr_check_iop_order] module not used but enabled!! %s %s(%d) image %i (%s)\n",
2418 mod->op, mod->multi_name, mod->iop_order,imgid, msg);
2419 }
2420 if(mod->multi_priority == 0)
2421 {
2422 iop_order_ok = 0;
2423 fprintf(stderr, "[dt_ioppr_check_iop_order] base module set as not used %s %s(%d) image %i (%s)\n",
2424 mod->op, mod->multi_name, mod->iop_order,imgid, msg);
2425 }
2426 }
2427 }
2428 }
2429
2430 // check if there's duplicate or out-of-order iop_order
2431 {
2432 dt_iop_module_t *mod_prev = NULL;
2433 for(const GList *modules = g_list_first(dev->iop); modules; modules = g_list_next(modules))
2434 {
2435 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
2436 if(mod->iop_order != INT_MAX)
2437 {
2438 if(mod_prev)
2439 {
2440 if(mod->iop_order < mod_prev->iop_order)
2441 {
2442 iop_order_ok = 0;
2443 fprintf(stderr,
2444 "[dt_ioppr_check_iop_order] module %s %s(%d) should be after %s %s(%d) image %i (%s)\n",
2445 mod->op, mod->multi_name, mod->iop_order, mod_prev->op, mod_prev->multi_name,
2446 mod_prev->iop_order, imgid, msg);
2447 }
2448 else if(mod->iop_order == mod_prev->iop_order)
2449 {
2450 iop_order_ok = 0;
2451 fprintf(
2452 stderr,
2453 "[dt_ioppr_check_iop_order] module %s %s(%i)(%d) and %s %s(%i)(%d) have the same order image %i (%s)\n",
2454 mod->op, mod->multi_name, mod->multi_priority, mod->iop_order, mod_prev->op,
2455 mod_prev->multi_name, mod_prev->multi_priority, mod_prev->iop_order, imgid, msg);
2456 }
2457 }
2458 }
2459 mod_prev = mod;
2460 }
2461 }
2462
2463 _ioppr_check_rules(dev->iop, imgid, msg);
2464
2465 for(const GList *history = dev->history; history; history = g_list_next(history))
2466 {
2467 const dt_dev_history_item_t *const restrict hist = (dt_dev_history_item_t *)(history->data);
2468
2469 if(hist->iop_order == INT_MAX)
2470 {
2471 if(hist->enabled)
2472 {
2473 iop_order_ok = 0;
2474 fprintf(stderr, "[dt_ioppr_check_iop_order] history module not used but enabled!! %s %s(%d) image %i (%s)\n",
2475 hist->op_name, hist->multi_name, hist->iop_order, imgid, msg);
2476 }
2477 if(hist->multi_priority == 0)
2478 {
2479 iop_order_ok = 0;
2480 fprintf(stderr, "[dt_ioppr_check_iop_order] history base module set as not used %s %s(%d) image %i (%s)\n",
2481 hist->op_name, hist->multi_name, hist->iop_order, imgid, msg);
2482 }
2483 }
2484 }
2485
2486 dt_print(DT_DEBUG_PARAMS, "[dt_ioppr_check_iop_order] IOP order passed (called from %s)\n", msg);
2487
2488 return iop_order_ok;
2489}
2490
2491void *dt_ioppr_serialize_iop_order_list(GList *iop_order_list, size_t *size)
2492{
2493 g_return_val_if_fail(!IS_NULL_PTR(iop_order_list), NULL);
2494 g_return_val_if_fail(!IS_NULL_PTR(size), NULL);
2495 // compute size of all modules
2496 *size = 0;
2497
2498 for(const GList *l = iop_order_list; l; l = g_list_next(l))
2499 {
2500 const dt_iop_order_entry_t *const restrict entry = (dt_iop_order_entry_t *)l->data;
2501 *size += strlen(entry->operation) + sizeof(int32_t) * 2;
2502 }
2503
2504 if(*size == 0)
2505 return NULL;
2506
2507 // allocate the parameter buffer
2508 char *params = (char *)malloc(*size);
2509
2510 // set set preset iop-order version
2511 int pos = 0;
2512
2513 for(const GList *l = iop_order_list; l; l = g_list_next(l))
2514 {
2515 const dt_iop_order_entry_t *const restrict entry = (dt_iop_order_entry_t *)l->data;
2516 // write the len of the module name
2517 const int32_t len = strlen(entry->operation);
2518 memcpy(params+pos, &len, sizeof(int32_t));
2519 pos += sizeof(int32_t);
2520
2521 // write the module name
2522 memcpy(params+pos, entry->operation, len);
2523 pos += len;
2524
2525 // write the instance number
2526 memcpy(params+pos, &(entry->instance), sizeof(int32_t));
2527 pos += sizeof(int32_t);
2528 }
2529
2530 return params;
2531}
2532
2533char *dt_ioppr_serialize_text_iop_order_list(GList *iop_order_list)
2534{
2535 GString *text = g_string_new("");
2536
2537 const GList *const last = g_list_last(iop_order_list);
2538 for(const GList *l = iop_order_list; l; l = g_list_next(l))
2539 {
2540 const dt_iop_order_entry_t *const restrict entry = (dt_iop_order_entry_t *)l->data;
2541 gchar buf[64];
2542 snprintf(buf, sizeof(buf), "%s,%d%s", entry->operation, entry->instance, (l == last) ? "" : ",");
2543 g_string_append(text, buf);
2544 }
2545
2546 return g_string_free(text, FALSE);
2547}
2548
2549/* this sanity check routine is used to correct wrong iop-list that
2550 * could have been stored while some bugs were present in
2551 * dartkable. There was a window around Sep 2019 where such issue
2552 * existed and some xmp may have been corrupt at this time making dt
2553 * crash while reimporting using the xmp.
2554 *
2555 * One common case seems that the list does not end with gamma.
2556*/
2557
2566static gboolean _ioppr_sanity_check_iop_order(GList *list)
2567{
2568 gboolean ok = TRUE;
2569
2570 // First check that first module is basebuffer (even for a jpeg, we
2571 // are speaking of the module ordering not the activated modules.
2572
2573 GList *first = g_list_first(list);
2574 dt_iop_order_entry_t *entry_first = (dt_iop_order_entry_t *)first->data;
2575
2576 ok = ok && (g_strcmp0(entry_first->operation, "basebuffer") == 0);
2577
2578 // Then check that last module is gamma
2579
2580 GList *last = g_list_last(list);
2581 dt_iop_order_entry_t *entry_last = (dt_iop_order_entry_t *)last->data;
2582
2583 ok = ok && (g_strcmp0(entry_last->operation, "gamma") == 0);
2584
2585 return ok;
2586}
2587
2589{
2590 GList *iop_order_list = NULL;
2591
2592 GList *list = dt_util_str_to_glist(",", buf);
2593 for(GList *l = list; l; l = g_list_next(l))
2594 {
2596 entry->o.iop_order = 0;
2597
2598 // first operation name
2599 g_strlcpy(entry->operation, (char *)l->data, sizeof(entry->operation));
2600
2601 // then operation instance
2602 l = g_list_next(l);
2603 if(IS_NULL_PTR(l)) goto error;
2604
2605 const char *data = (char *)l->data;
2606 int inst = 0;
2607 sscanf(data, "%d", &inst);
2608 entry->instance = inst;
2609
2610 // append to the list
2611 iop_order_list = g_list_prepend(iop_order_list, entry);
2612 }
2613 iop_order_list = g_list_reverse(iop_order_list); // list was built in reverse order, so un-reverse it
2614
2615 g_list_free_full(list, dt_free_gpointer);
2616 list = NULL;
2617
2618 _ioppr_reset_iop_order(iop_order_list);
2619
2620 // Remove sanity check from here; it's now handled after migration in dt_ioppr_get_iop_order_list()
2621
2622 return iop_order_list;
2623
2624 error:
2625 g_list_free_full(list, dt_free_gpointer);
2626 list = NULL;
2627 g_list_free_full(iop_order_list, dt_free_gpointer);
2628 iop_order_list = NULL;
2629 return NULL;
2630}
2631
2632GList *dt_ioppr_deserialize_iop_order_list(const char *buf, size_t size)
2633{
2634 GList *iop_order_list = NULL;
2635
2636 // parse all modules
2637 while(size)
2638 {
2640
2641 entry->o.iop_order = 0;
2642
2643 // get length of module name
2644 const int32_t len = *(int32_t *)buf;
2645 buf += sizeof(int32_t);
2646
2647 if(len < 0 || len > 20) { dt_free(entry); goto error; }
2648
2649 // set module name
2650 memcpy(entry->operation, buf, len);
2651 *(entry->operation + len) = '\0';
2652 buf += len;
2653
2654 // get the instance number
2655 entry->instance = *(int32_t *)buf;
2656 buf += sizeof(int32_t);
2657
2658 if(entry->instance < 0 || entry->instance > 1000) { dt_free(entry); goto error; }
2659
2660 // append to the list
2661 iop_order_list = g_list_prepend(iop_order_list, entry);
2662
2663 size -= (2 * sizeof(int32_t) + len);
2664 }
2665 iop_order_list = g_list_reverse(iop_order_list); // list was built in reverse order, so un-reverse it
2666
2667 _ioppr_reset_iop_order(iop_order_list);
2668
2669 return iop_order_list;
2670
2671 error:
2672 g_list_free_full(iop_order_list, dt_free_gpointer);
2673 iop_order_list = NULL;
2674 return NULL;
2675}
2676
2677#undef DT_IOP_ORDER_INFO
2678// clang-format off
2679// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
2680// vim: shiftwidth=2 expandtab tabstop=2 cindent
2681// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
2682// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
static const dt_adaptation_t kind
gboolean dt_image_needs_rawprepare(const dt_image_t *img)
darktable_t darktable
Definition darktable.c:181
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1542
@ DT_DEBUG_PARAMS
Definition darktable.h:736
static void dt_free_gpointer(gpointer ptr)
Definition darktable.h:463
#define dt_free(ptr)
Definition darktable.h:456
#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 darktable.h:281
static gboolean g_list_shorter_than(const GList *list, unsigned len)
Definition darktable.h:939
sqlite3 * dt_database_get(const dt_database_t *db)
Definition database.c:3646
#define DT_DEBUG_SQLITE3_PREPARE_V2(a, b, c, d, e)
Definition debug.h:107
#define DT_DEBUG_SQLITE3_BIND_TEXT(a, b, c, d, e)
Definition debug.h:118
#define DT_DEBUG_SQLITE3_BIND_INT(a, b, c)
Definition debug.h:115
int dt_deprecated(const char *op)
Modules without a proper IOP order should throw errors and log, except if they are deprecated definit...
void dt_dev_write_history(dt_develop_t *dev, gboolean async)
Thread-safe wrapper around dt_dev_write_history_ext() for dev->image_storage.id.
void dt_dev_history_pixelpipe_update(dt_develop_t *dev, gboolean rebuild)
Rebuild or resync pixelpipes after backend history changes.
void dt_dev_history_gui_update(dt_develop_t *dev)
Apply history-loaded params to module GUIs.
void dt_dev_history_notify_change(dt_develop_t *dev, const int32_t imgid)
Notify the rest of the app that history changes were written.
gboolean dt_dev_reload_history_items(dt_develop_t *dev, const int32_t imgid)
Reload history from DB and rebuild pipelines/GUI state.
void dt_image_cache_read_release(dt_image_cache_t *cache, const dt_image_t *img)
dt_image_t * dt_image_cache_testget(dt_image_cache_t *cache, const int32_t imgid, char mode)
dt_iop_module_t * dt_iop_get_module_by_instance_name(GList *modules, const char *operation, const char *multi_name)
Definition imageop.c:3099
dt_iop_module_t * dt_iop_get_module_by_op_priority(GList *modules, const char *operation, const int multi_priority)
Definition imageop.c:3081
const dt_iop_order_entry_t ansel_raw_order[]
Definition iop_order.c:559
static void dt_ioppr_check_duplicate_iop_order(GList **_iop_list, GList *history_list)
Detect and resolve duplicate iop_order values.
Definition iop_order.c:1859
static GList * dt_ioppr_extract_multi_instances_list(GList *iop_order_list)
Extract all order entries that have multiple instances.
Definition iop_order.c:1407
const dt_iop_order_entry_t v30_order[]
Definition iop_order.c:191
static void _ioppr_check_rules(GList *iop_list, const int32_t imgid, const char *msg)
Validate pipeline order against fence and rule constraints.
Definition iop_order.c:2295
GList * dt_ioppr_get_iop_order_list(int32_t imgid, gboolean sorted)
Load the order list for an image from the DB.
Definition iop_order.c:1096
gboolean dt_ioppr_has_multiple_instances(GList *iop_order_list)
Detect whether multiple instances are grouped for a non-custom order.
Definition iop_order.c:945
gboolean dt_ioppr_has_iop_order_list(int32_t imgid)
Check whether the image has an explicit order list stored in DB.
Definition iop_order.c:1073
static gboolean _ioppr_sanity_check_iop_order(GList *list)
Basic sanity check for an order list.
Definition iop_order.c:2566
GList * dt_ioppr_merge_module_multi_instance_iop_order_list(GList *iop_order_list, const char *operation, GList *multi_instance_list)
Merge an operation's multiple instances into the order list.
Definition iop_order.c:1435
void * dt_ioppr_serialize_iop_order_list(GList *iop_order_list, size_t *size)
Serialize an order list into a binary blob (used for presets).
Definition iop_order.c:2491
GList * dt_ioppr_get_iop_order_list_version(dt_iop_order_t version)
Return the built-in order list for a given version.
Definition iop_order.c:1044
gint dt_sort_iop_list_by_order_f(gconstpointer a, gconstpointer b)
Compare two list nodes holding modules by iop_order.
Definition iop_order.c:893
static void * _dup_iop_order_entry(const void *src, gpointer data)
Deep-copy callback for dt_iop_order_entry_t.
Definition iop_order.c:1987
gint dt_sort_iop_by_order(gconstpointer a, gconstpointer b)
Compare two module instances by iop_order for sorting.
Definition iop_order.c:2002
static void _ioppr_reset_iop_order(GList *iop_order_list)
Reset iop_order values to a sequential order for a list.
Definition iop_order.c:1224
GList * dt_ioppr_get_iop_order_rules()
Return the list of ordering rules (prev/next constraints).
Definition iop_order.c:793
static void dt_ioppr_migrate_iop_order(struct dt_develop_t *dev, const int32_t imgid)
Apply a new order list by reloading history and rebuilding UI/pipelines.
Definition iop_order.c:1377
static dt_dev_history_item_t * _ioppr_search_history_by_module(GList *history_list, dt_iop_module_t *mod)
Find a history item referencing a given module instance.
Definition iop_order.c:1830
static int _count_entries_operation(GList *e_list, const char *operation)
Count order-list entries matching an operation name.
Definition iop_order.c:1587
static GList * _insert_before(GList *iop_order_list, const char *module, const char *new_module)
Insert a missing module entry before another module in an order list.
Definition iop_order.c:720
void dt_ioppr_rebuild_iop_order_from_modules(struct dt_develop_t *dev, GList *ordered_modules)
Rebuild dev->iop_order_list from a list of ordered modules.
Definition iop_order.c:1294
gint dt_sort_iop_list_by_order(gconstpointer a, gconstpointer b)
Definition iop_order.c:884
GList * _table_to_list(const dt_iop_order_entry_t entries[])
Build an order list from a static entry table.
Definition iop_order.c:1025
gboolean dt_ioppr_check_can_move_before_iop(GList *iop_list, dt_iop_module_t *module, dt_iop_module_t *module_next)
Validate whether module can be moved before module_next.
Definition iop_order.c:2015
gboolean dt_ioppr_check_can_move_after_iop(GList *iop_list, dt_iop_module_t *module, dt_iop_module_t *module_prev)
Validate whether module can be moved after module_prev.
Definition iop_order.c:2184
gboolean dt_ioppr_move_iop_after(struct dt_develop_t *dev, dt_iop_module_t *module, dt_iop_module_t *module_prev)
Move a module instance after another module in the pipe.
Definition iop_order.c:2251
static GList * dt_ioppr_merge_multi_instance_iop_order_list(GList *iop_order_list, GList *multi_instance_list)
Merge multiple-instance entries into an order list.
Definition iop_order.c:1498
const dt_iop_order_entry_t ansel_jpg_order[]
Definition iop_order.c:414
static gboolean dt_ioppr_write_iop_order(const dt_iop_order_t kind, GList *iop_order_list, const int32_t imgid)
Persist an order list for a given image with a specific kind.
Definition iop_order.c:974
GList * dt_ioppr_iop_order_copy_deep(GList *iop_order_list)
Deep-copy an order list.
Definition iop_order.c:1996
gboolean dt_ioppr_write_iop_order_list(GList *iop_order_list, const int32_t imgid)
Persist an order list to the DB for a given image.
Definition iop_order.c:1013
void dt_ioppr_resync_modules_order(dt_develop_t *dev)
Update dev->iop module order values from dev->iop_order_list.
Definition iop_order.c:1271
int dt_ioppr_get_iop_order(GList *iop_order_list, const char *op_name, const int multi_priority)
Return the iop_order for a given operation/instance pair.
Definition iop_order.c:868
int _get_multi_priority(dt_develop_t *dev, const char *operation, const int n, const gboolean only_disabled)
Return the multi_priority of the n-th instance of an operation.
Definition iop_order.c:1626
int dt_ioppr_check_so_iop_order(GList *iop_list, GList *iop_order_list)
Check whether any module .so is missing an iop_order entry.
Definition iop_order.c:1960
const dt_iop_order_entry_t legacy_order[]
Definition iop_order.c:95
static const dt_iop_order_entry_t * orders[5]
Definition iop_order.c:903
const char * iop_order_string[]
Definition iop_order.c:70
gboolean dt_ioppr_move_iop_before(struct dt_develop_t *dev, dt_iop_module_t *module, dt_iop_module_t *module_next)
Move a module instance before another module in the pipe.
Definition iop_order.c:2219
dt_iop_order_t dt_ioppr_get_iop_order_list_kind(GList *iop_order_list)
Determine the kind of an order list by inspecting its content.
Definition iop_order.c:905
void dt_ioppr_update_for_style_items(dt_develop_t *dev, GList *st_items, gboolean append)
Update dev->iop_order_list with modules referenced by style items.
Definition iop_order.c:1747
void dt_ioppr_set_default_iop_order(dt_develop_t *dev, const int32_t imgid)
Set dev->iop_order_list to the default order for a given image.
Definition iop_order.c:1325
void dt_ioppr_resync_pipeline(dt_develop_t *dev, const int32_t imgid, const char *msg, gboolean check_duplicates)
Resynchronize pipeline order and related structures.
Definition iop_order.c:1263
char * dt_ioppr_serialize_text_iop_order_list(GList *iop_order_list)
Serialize an order list to a text representation.
Definition iop_order.c:2533
const dt_iop_order_entry_t v30_jpg_order[]
Definition iop_order.c:302
GList * dt_ioppr_deserialize_text_iop_order_list(const char *buf)
Deserialize an order list from a text representation.
Definition iop_order.c:2588
void dt_ioppr_insert_module_instance(struct dt_develop_t *dev, dt_iop_module_t *module)
Ensure a module instance has an entry in dev->iop_order_list.
Definition iop_order.c:2349
const char * dt_iop_order_string(const dt_iop_order_t order)
Return the human-readable name for an IOP order enum value.
Definition iop_order.c:80
void dt_ioppr_update_for_modules(dt_develop_t *dev, GList *modules, gboolean append)
Update dev->iop_order_list with modules from a module list.
Definition iop_order.c:1784
static dt_iop_order_entry_t * dt_ioppr_get_iop_order_entry(GList *iop_order_list, const char *op_name, const int multi_priority)
Return the first order entry matching operation/instance.
Definition iop_order.c:857
static gboolean _operation_already_handled(GList *e_list, const char *operation)
Check if an operation was already handled earlier in the list.
Definition iop_order.c:1607
dt_iop_order_t dt_ioppr_get_iop_order_version(const int32_t imgid)
Fetch the IOP order version stored for an image.
Definition iop_order.c:761
static void dt_ioppr_resync_iop_list(dt_develop_t *dev)
Resynchronize dev->iop list order against dev->iop_order_list.
Definition iop_order.c:1243
GList * dt_ioppr_deserialize_iop_order_list(const char *buf, size_t size)
Deserialize an order list from a binary blob.
Definition iop_order.c:2632
int dt_ioppr_check_iop_order(dt_develop_t *dev, const int32_t imgid, const char *msg)
Debug helper to validate the current order for a develop context.
Definition iop_order.c:2377
static void _count_iop_module(GList *iop, const char *operation, int *max_multi_priority, int *count, int *max_multi_priority_enabled, int *count_enabled)
Count module instances and track their highest priorities.
Definition iop_order.c:1553
static void dt_ioppr_update_for_entries(dt_develop_t *dev, GList *entry_list, gboolean append)
Update dev->iop_order_list to include entries from entry_list.
Definition iop_order.c:1651
void dt_ioppr_change_iop_order(struct dt_develop_t *dev, const int32_t imgid, GList *new_iop_list)
Replace the current order list with a new one and persist it.
Definition iop_order.c:1386
GList * dt_ioppr_get_iop_order_link(GList *iop_order_list, const char *op_name, const int multi_priority)
Find a list link matching an operation and instance.
Definition iop_order.c:830
dt_iop_order_t
Definition iop_order.h:143
@ DT_IOP_ORDER_ANSEL_RAW
Definition iop_order.h:148
@ DT_IOP_ORDER_LEGACY
Definition iop_order.h:145
@ DT_IOP_ORDER_LAST
Definition iop_order.h:150
@ DT_IOP_ORDER_V30_JPG
Definition iop_order.h:147
@ DT_IOP_ORDER_V30
Definition iop_order.h:146
@ DT_IOP_ORDER_CUSTOM
Definition iop_order.h:144
@ DT_IOP_ORDER_ANSEL_JPG
Definition iop_order.h:149
float *const restrict const size_t k
size_t size
Definition mipmap_cache.c:3
void copy(double *dest, double *source, size_t num_el)
Copy a flat buffer.
const struct dt_database_t * db
Definition darktable.h:779
GList * iop_order_rules
Definition darktable.h:763
struct dt_image_cache_t * image_cache
Definition darktable.h:777
struct dt_develop_t * develop
Definition darktable.h:770
GList * iop_order_list
Definition develop.h:285
dt_image_t image_storage
Definition develop.h:259
GList * iop
Definition develop.h:279
GList * history
Definition develop.h:275
int32_t id
Definition image.h:319
struct dt_iop_module_t::@31 raster_mask
char multi_name[128]
Definition imageop.h:363
struct dt_iop_module_t::@31::@32 source
GModule *dt_dev_operation_t op
Definition imageop.h:256
gboolean enabled
Definition imageop.h:298
struct dt_iop_module_t::@31::@33 sink
Definition iop_order.h:154
char name[25]
Definition iop_order.h:162
int iop_order
Definition iop_order.h:157
char operation[20]
Definition iop_order.h:160
union dt_iop_order_entry_t::@8 o
double iop_order_f
Definition iop_order.h:156
int32_t instance
Definition iop_order.h:161
#define MAX(a, b)
Definition thinplate.c:29
GList * dt_util_str_to_glist(const gchar *separator, const gchar *text)
Definition utility.c:830