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 = "highlights", .op_next = "cacorrect" },
802 { .op_prev = "cacorrect", .op_next = "hotpixels" },
803 { .op_prev = "hotpixels", .op_next = "rawdenoise" },
804 { .op_prev = "rawdenoise", .op_next = "demosaic" },
805 { .op_prev = "demosaic", .op_next = "colorin" },
806 { .op_prev = "colorin", .op_next = "colorout" },
807 { .op_prev = "colorout", .op_next = "gamma" },
808 { .op_prev = "flip", .op_next = "crop" }, // crop GUI broken if flip is done on top
809 { .op_prev = "flip", .op_next = "clipping" }, // clipping GUI broken if flip is done on top
810 { .op_prev = "ashift", .op_next = "clipping" }, // clipping GUI broken if ashift is done on top
811 { .op_prev = "colorin", .op_next = "channelmixerrgb"},
812 { "\0", "\0" } };
813
814 int i = 0;
815 while(rule_entry[i].op_prev[0])
816 {
817 dt_iop_order_rule_t *rule = calloc(1, sizeof(dt_iop_order_rule_t));
818
819 memcpy(rule->op_prev, rule_entry[i].op_prev, sizeof(rule->op_prev));
820 memcpy(rule->op_next, rule_entry[i].op_next, sizeof(rule->op_next));
821
822 rules = g_list_prepend(rules, rule);
823 i++;
824 }
825
826 return g_list_reverse(rules); // list was built in reverse order, so un-reverse it
827}
828
829GList *dt_ioppr_get_iop_order_link(GList *iop_order_list, const char *op_name, const int multi_priority)
830{
831 GList *link = NULL;
832
833 for(GList *iops_order = iop_order_list; iops_order; iops_order = g_list_next(iops_order))
834 {
835 dt_iop_order_entry_t *order_entry = (dt_iop_order_entry_t *)iops_order->data;
836
837 if(strcmp(order_entry->operation, op_name) == 0
838 && (order_entry->instance == multi_priority || multi_priority == -1))
839 {
840 link = iops_order;
841 break;
842 }
843 }
844
845 return link;
846}
847
856static dt_iop_order_entry_t *dt_ioppr_get_iop_order_entry(GList *iop_order_list, const char *op_name,
857 const int multi_priority)
858{
859 const GList * const restrict link = dt_ioppr_get_iop_order_link(iop_order_list, op_name, multi_priority);
860 if(link)
861 return (dt_iop_order_entry_t *)link->data;
862 else
863 return NULL;
864}
865
866// returns the iop_order associated with the iop order entry that matches operation == op_name
867int dt_ioppr_get_iop_order(GList *iop_order_list, const char *op_name, const int multi_priority)
868{
869 int iop_order = INT_MAX;
870 const dt_iop_order_entry_t *const restrict order_entry =
871 dt_ioppr_get_iop_order_entry(iop_order_list, op_name, multi_priority);
872
873 if(order_entry)
874 {
875 iop_order = order_entry->o.iop_order;
876 }
877 else if(!dt_deprecated(op_name))
878 fprintf(stderr, "[iop_order] %s instance %d is missing from the saved pipeline order; "
879 "its position will be sanitized to the default order\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 parked at INT_MAX are "not in pipe" (non-visible) and stay parked so
1284 // _update_iop_visibility can remove them. But an ENABLED module must always occupy a
1285 // real pipe position: leaving it at INT_MAX drops it after gamma and ties it with the
1286 // other parked modules, so an unstable sort can wedge it into a wrong slot where the
1287 // pixelpipe format pass disables it for "unexpected input buffer format" (issue #961).
1288 // This happens to enabled extra instances (multi_priority > 0) whose history entry
1289 // carried a stale INT_MAX order. Re-derive their order from the authoritative order
1290 // list; a genuinely-orphan instance still falls back to INT_MAX from the lookup.
1291 if(mod->iop_order != INT_MAX || mod->enabled)
1293
1294 modules = next;
1295 }
1296
1297 dev->iop = g_list_sort(dev->iop, dt_sort_iop_by_order);
1298}
1299
1300void dt_ioppr_rebuild_iop_order_from_modules(struct dt_develop_t *dev, GList *ordered_modules)
1301{
1302 if(IS_NULL_PTR(dev) || !ordered_modules) return;
1303
1304 GList *new_iop_order_list = NULL;
1305 int order = 1;
1306
1307 for(const GList *l = ordered_modules; l; l = g_list_next(l))
1308 {
1309 const dt_iop_module_t *mod = (const dt_iop_module_t *)l->data;
1310 if(IS_NULL_PTR(mod)) continue;
1311
1312 dt_iop_order_entry_t *entry = (dt_iop_order_entry_t *)calloc(1, sizeof(dt_iop_order_entry_t));
1313 g_strlcpy(entry->operation, mod->op, sizeof(entry->operation));
1314 entry->instance = mod->multi_priority;
1315 g_strlcpy(entry->name, mod->multi_name, sizeof(entry->name));
1316 entry->o.iop_order = order++;
1317 new_iop_order_list = g_list_append(new_iop_order_list, entry);
1318 }
1319
1320 if(new_iop_order_list)
1321 {
1322 g_list_free_full(dev->iop_order_list, dt_free_gpointer);
1323 dev->iop_order_list = new_iop_order_list;
1325 }
1326}
1327
1328// sets the iop_order on each module of *_iop_list
1329// iop_order is set only for base modules, multi-instances will be flagged as unused with INT_MAX
1330// if a module do not exists on iop_order_list it is flagged as unused with INT_MAX
1331void dt_ioppr_set_default_iop_order(dt_develop_t *dev, const int32_t imgid)
1332{
1333 // First check whether the image already owns an order in DB. If it does not,
1334 // choose the built-in order from dev->image_storage, which was just refreshed
1335 // by the darkroom/history caller and is the image state used by module reloads.
1336 gboolean has_stored_order = FALSE;
1337 if(imgid > 0)
1338 {
1339 sqlite3_stmt *stmt;
1341 "SELECT 1 FROM main.module_order WHERE imgid = ?1", -1,
1342 &stmt, NULL);
1343 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
1344 has_stored_order = (sqlite3_step(stmt) == SQLITE_ROW);
1345 sqlite3_finalize(stmt);
1346 }
1347
1348 GList *iop_order_list = NULL;
1349 if(!has_stored_order && dev->image_storage.id == imgid)
1350 {
1351 const dt_iop_order_t default_order = dt_image_needs_rawprepare(&dev->image_storage)
1354 iop_order_list = dt_ioppr_get_iop_order_list_version(default_order);
1355 }
1356 else
1357 {
1358 iop_order_list = dt_ioppr_get_iop_order_list(imgid, FALSE);
1359 }
1360
1361 // we assign a single iop-order to each module
1362
1363 _ioppr_reset_iop_order(iop_order_list);
1364
1365 if(dev->iop_order_list)
1366 {
1367 g_list_free_full(dev->iop_order_list, dt_free_gpointer);
1368 dev->iop_order_list = NULL;
1369 }
1370 dev->iop_order_list = iop_order_list;
1371
1372 // we now set the module list given to this iop-order
1373
1375}
1376
1383static void dt_ioppr_migrate_iop_order(struct dt_develop_t *dev, const int32_t imgid)
1384{
1389 dt_dev_history_notify_change(dev, imgid);
1390}
1391
1392void dt_ioppr_change_iop_order(struct dt_develop_t *dev, const int32_t imgid, GList *new_iop_list)
1393{
1394 GList *iop_list = dt_ioppr_iop_order_copy_deep(new_iop_list);
1396
1397 if(mi) iop_list = dt_ioppr_merge_multi_instance_iop_order_list(iop_list, mi);
1398
1401 g_list_free_full(iop_list, dt_free_gpointer);
1402 iop_list = NULL;
1403
1405}
1406
1413static GList *dt_ioppr_extract_multi_instances_list(GList *iop_order_list)
1414{
1415 GList *mi = NULL;
1416
1417 for(const GList *l = iop_order_list; l; l = g_list_next(l))
1418 {
1419 const dt_iop_order_entry_t *const restrict entry = (dt_iop_order_entry_t *)l->data;
1420
1421 if(_count_entries_operation(iop_order_list, entry->operation) > 1)
1422 {
1424 mi = g_list_prepend(mi, copy);
1425 }
1426 }
1427
1428 return g_list_reverse(mi); // list was built in reverse order, so un-reverse it
1429}
1430
1442 const char *operation, GList *multi_instance_list)
1443{
1444 const int count_to = _count_entries_operation(iop_order_list, operation);
1445
1446 int item_nb = 0;
1447
1448 GList *link = iop_order_list;
1449
1450 for(const GList *l = multi_instance_list; l; l = g_list_next(l))
1451 {
1452 dt_iop_order_entry_t *entry = (dt_iop_order_entry_t *)l->data;
1453
1454 item_nb++;
1455
1456 if(item_nb <= count_to)
1457 {
1458 link = dt_ioppr_get_iop_order_link(link, operation, -1);
1459 dt_iop_order_entry_t *e = (dt_iop_order_entry_t *)link->data;
1460 e->instance = entry->instance;
1461
1462 // free this entry as not merged into the list
1463 dt_free(entry);
1464
1465 // next replace should happen to any module after this one
1466 link = g_list_next(link);
1467 }
1468 else
1469 {
1470 iop_order_list = g_list_insert_before(iop_order_list, link, entry);
1471 }
1472 }
1473
1474 // if needed removes all other instance of this operation which are superfluous
1475 if(g_list_shorter_than(multi_instance_list, count_to))
1476 {
1477 while(link)
1478 {
1479 const dt_iop_order_entry_t *const restrict entry = (dt_iop_order_entry_t *)link->data;
1480 GList *next = g_list_next(link);
1481 if(strcmp(operation, entry->operation) == 0)
1482 {
1483 dt_free(link->data);
1484 iop_order_list = g_list_delete_link(iop_order_list, link);
1485 }
1486
1487 link = next;
1488 }
1489 }
1490
1491 return iop_order_list;
1492}
1493
1504static GList *dt_ioppr_merge_multi_instance_iop_order_list(GList *iop_order_list, GList *multi_instance_list)
1505{
1506 GList *op = NULL;
1507
1508 GList *copy = dt_ioppr_iop_order_copy_deep(multi_instance_list);
1509 GList *l = copy;
1510
1511 while(l)
1512 {
1513 dt_iop_order_entry_t *entry = (dt_iop_order_entry_t *)l->data;
1514 GList *l_next = g_list_next(l);
1515
1516 op = g_list_append(op, entry);
1517
1518 copy = g_list_delete_link(copy, l);
1519
1520 GList *mi = l_next;
1521 while(mi)
1522 {
1523 GList *next = g_list_next(mi);
1524 dt_iop_order_entry_t *mi_entry = (dt_iop_order_entry_t *)mi->data;
1525 if(strcmp(entry->operation, mi_entry->operation) == 0)
1526 {
1527 op = g_list_append(op, mi_entry);
1528 copy = g_list_delete_link(copy, mi);
1529 }
1530
1531 mi = next;
1532 }
1533
1534 // copy operation as entry may be freed
1535 char operation[20];
1536 memcpy(operation, entry->operation, sizeof(entry->operation));
1537
1538 iop_order_list = dt_ioppr_merge_module_multi_instance_iop_order_list(iop_order_list, operation, op);
1539
1540 g_list_free(op);
1541 op = NULL;
1542
1543 l = copy;
1544 }
1545
1546 return iop_order_list;
1547}
1548
1559static void _count_iop_module(GList *iop, const char *operation, int *max_multi_priority, int *count,
1560 int *max_multi_priority_enabled, int *count_enabled)
1561{
1562 *max_multi_priority = 0;
1563 *count = 0;
1564 *max_multi_priority_enabled = 0;
1565 *count_enabled = 0;
1566
1567 for(const GList *modules = iop; modules; modules = g_list_next(modules))
1568 {
1569 const dt_iop_module_t *const restrict mod = (dt_iop_module_t *)modules->data;
1570 if(!strcmp(mod->op, operation))
1571 {
1572 (*count)++;
1573 if(*max_multi_priority < mod->multi_priority) *max_multi_priority = mod->multi_priority;
1574
1575 if(mod->enabled)
1576 {
1577 (*count_enabled)++;
1578 if(*max_multi_priority_enabled < mod->multi_priority) *max_multi_priority_enabled = mod->multi_priority;
1579 }
1580 }
1581 }
1582
1583 assert(*count >= *count_enabled);
1584}
1585
1593static int _count_entries_operation(GList *e_list, const char *operation)
1594{
1595 int count = 0;
1596
1597 for(const GList *l = e_list; l; l = g_list_next(l))
1598 {
1600 if(!strcmp(ep->operation, operation)) count++;
1601 }
1602
1603 return count;
1604}
1605
1613static gboolean _operation_already_handled(GList *e_list, const char *operation)
1614{
1615 for(const GList *l = g_list_previous(e_list); l; l = g_list_previous(l))
1616 {
1617 const dt_iop_order_entry_t *const restrict ep = (dt_iop_order_entry_t *)l->data;
1618 if(!strcmp(ep->operation, operation)) return TRUE;
1619 }
1620 return FALSE;
1621}
1622
1632int _get_multi_priority(dt_develop_t *dev, const char *operation, const int n, const gboolean only_disabled)
1633{
1634 int count = 0;
1635 for(const GList *l = dev->iop; l; l = g_list_next(l))
1636 {
1637 const dt_iop_module_t *const restrict mod = (dt_iop_module_t *)l->data;
1638 if((!only_disabled || mod->enabled == FALSE) && !strcmp(mod->op, operation))
1639 {
1640 count++;
1641 if(count == n) return mod->multi_priority;
1642 }
1643 }
1644
1645 return INT_MAX;
1646}
1647
1657static void dt_ioppr_update_for_entries(dt_develop_t *dev, GList *entry_list, gboolean append)
1658{
1659
1660 // for each priority list to be checked
1661 for(GList *e_list = entry_list; e_list; e_list = g_list_next(e_list))
1662 {
1663 const dt_iop_order_entry_t *const restrict ep = (dt_iop_order_entry_t *)e_list->data;
1664
1665 gboolean force_append = FALSE;
1666
1667 // we also need to force append (even if overwrite mode is
1668 // selected - append = FALSE) when a module has a specific name
1669 // and this name is not present into the current iop list.
1670
1671 if(*ep->name && !dt_iop_get_module_by_instance_name(dev->iop, ep->operation, ep->name))
1672 force_append = TRUE;
1673
1674 int max_multi_priority = 0, count = 0;
1675 int max_multi_priority_enabled = 0, count_enabled = 0;
1676
1677 // is it a currently active module and if so how many active instances we have
1678 _count_iop_module(dev->iop, ep->operation,
1679 &max_multi_priority, &count, &max_multi_priority_enabled, &count_enabled);
1680
1681 // look for this operation into the target iop-order list and add there as much operation as needed
1682
1683 for(GList *l = g_list_last(dev->iop_order_list); l; l = g_list_previous(l))
1684 {
1685 const dt_iop_order_entry_t *const restrict e = (dt_iop_order_entry_t *)l->data;
1686 if(!strcmp(e->operation, ep->operation) && !_operation_already_handled(e_list, ep->operation))
1687 {
1688 // how many instances of this module in the entry list, and re-number multi-priority accordingly
1689 const int new_active_instances = _count_entries_operation(entry_list, ep->operation);
1690
1691 int add_count = 0;
1692 int start_multi_priority = 0;
1693 int nb_replace = 0;
1694
1695 if(append || force_append)
1696 {
1697 nb_replace = count - count_enabled;
1698 add_count = MAX(0, new_active_instances - nb_replace);
1699 start_multi_priority = max_multi_priority + 1;
1700 }
1701 else
1702 {
1703 nb_replace = count;
1704 add_count = MAX(0, new_active_instances - count);
1705 start_multi_priority = max_multi_priority + 1;
1706 }
1707
1708 // update multi_priority to be unique in iop list
1709 int multi_priority = start_multi_priority;
1710 int nb = 0;
1711
1712 for(const GList *s = entry_list; s; s = g_list_next(s))
1713 {
1714 dt_iop_order_entry_t *item = (dt_iop_order_entry_t *)s->data;
1715 if(!strcmp(item->operation, e->operation))
1716 {
1717 nb++;
1718 if(nb <= nb_replace)
1719 {
1720 // this one replaces current module, get it's multi-priority
1721 item->instance = _get_multi_priority(dev, item->operation, nb, append);
1722 }
1723 else
1724 {
1725 // otherwise create a new multi-priority
1726 item->instance = multi_priority++;
1727 }
1728 }
1729 }
1730
1731 multi_priority = start_multi_priority;
1732
1733 l = g_list_next(l);
1734
1735 for(int k = 0; k<add_count; k++)
1736 {
1738 g_strlcpy(n->operation, ep->operation, sizeof(n->operation));
1739 n->instance = multi_priority++;
1740 n->o.iop_order = 0;
1741 dev->iop_order_list = g_list_insert_before(dev->iop_order_list, l, n);
1742 }
1743 break;
1744 }
1745 }
1746 }
1747
1749
1750// dt_ioppr_print_iop_order(dev->iop_order_list, "upd sitem");
1751}
1752
1753void dt_ioppr_update_for_style_items(dt_develop_t *dev, GList *st_items, gboolean append)
1754{
1755 GList *e_list = NULL;
1756
1757 // for each priority list to be checked
1758 for(const GList *si_list = st_items; si_list; si_list = g_list_next(si_list))
1759 {
1760 const dt_style_item_t *const restrict si = (dt_style_item_t *)si_list->data;
1761
1763 memcpy(n->operation, si->operation, sizeof(n->operation));
1764 n->instance = si->multi_priority;
1765 g_strlcpy(n->name, si->multi_name, sizeof(n->name));
1766 n->o.iop_order = 0;
1767 e_list = g_list_prepend(e_list, n);
1768 }
1769 e_list = g_list_reverse(e_list); // list was built in reverse order, so un-reverse it
1770
1771 dt_ioppr_update_for_entries(dev, e_list, append);
1772
1773 // write back the multi-priority
1774
1775 GList *el = e_list;
1776 for(const GList *si_list = st_items; si_list; si_list = g_list_next(si_list))
1777 {
1778 dt_style_item_t *si = (dt_style_item_t *)si_list->data;
1779 const dt_iop_order_entry_t *const restrict e = (dt_iop_order_entry_t *)el->data;
1780
1781 si->multi_priority = e->instance;
1783 el = g_list_next(el);
1784 }
1785
1786 g_list_free(e_list);
1787 e_list = NULL;
1788}
1789
1790void dt_ioppr_update_for_modules(dt_develop_t *dev, GList *modules, gboolean append)
1791{
1792 GList *e_list = NULL;
1793
1794 // for each priority list to be checked
1795 for(const GList *m_list = modules; m_list; m_list = g_list_next(m_list))
1796 {
1797 const dt_iop_module_t *const restrict mod = (dt_iop_module_t *)m_list->data;
1798
1800 g_strlcpy(n->operation, mod->op, sizeof(n->operation));
1801 n->instance = mod->multi_priority;
1802 g_strlcpy(n->name, mod->multi_name, sizeof(n->name));
1803 n->o.iop_order = 0;
1804 e_list = g_list_prepend(e_list, n);
1805 }
1806 e_list = g_list_reverse(e_list); // list was built in reverse order, so un-reverse it
1807
1808 dt_ioppr_update_for_entries(dev, e_list, append);
1809
1810 // write back the multi-priority
1811
1812 GList *el = e_list;
1813 for(const GList *m_list = modules; m_list; m_list = g_list_next(m_list))
1814 {
1815 dt_iop_module_t *mod = (dt_iop_module_t *)m_list->data;
1817
1818 mod->multi_priority = e->instance;
1820
1821 el = g_list_next(el);
1822 }
1823
1824 g_list_free_full(e_list, dt_free_gpointer);
1825 e_list = NULL;
1826}
1827
1828// returns the first dt_dev_history_item_t on history_list where hist->module == mod
1837{
1838 dt_dev_history_item_t *hist_entry = NULL;
1839
1840 for(const GList *history = history_list; history; history = g_list_next(history))
1841 {
1842 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
1843
1844 if(hist->module == mod)
1845 {
1846 hist_entry = hist;
1847 break;
1848 }
1849 }
1850
1851 return hist_entry;
1852}
1853
1854// check if there's duplicate iop_order entries in iop_list
1855// if so, updates the iop_order to be unique, but only if the module is disabled and not in history
1865static void dt_ioppr_check_duplicate_iop_order(GList **_iop_list, GList *history_list)
1866{
1867 GList *iop_list = *_iop_list;
1868 dt_iop_module_t *mod_prev = NULL;
1869
1870 // get the first module
1871 GList *modules = iop_list;
1872 if(modules)
1873 {
1874 mod_prev = (dt_iop_module_t *)(modules->data);
1875 modules = g_list_next(modules);
1876 }
1877 // check for each module if iop_order is the same as the previous one
1878 // if so, change it, but only if disabled and not in history
1879 while(modules)
1880 {
1881 int reset_list = 0;
1882 dt_iop_module_t *mod = (dt_iop_module_t *)(modules->data);
1883
1884 if(mod->iop_order == mod_prev->iop_order && mod->iop_order != INT_MAX)
1885 {
1886 int can_move = 0;
1887
1888 if(!mod->enabled && _ioppr_search_history_by_module(history_list, mod) == NULL)
1889 {
1890 can_move = 1;
1891
1892 GList *modules1 = g_list_next(modules);
1893 if(modules1)
1894 {
1895 dt_iop_module_t *mod_next = (dt_iop_module_t *)(modules1->data);
1896 if(mod->iop_order != mod_next->iop_order)
1897 {
1898 mod->iop_order += (mod_next->iop_order - mod->iop_order) / 2.0;
1899 }
1900 else
1901 {
1902 dt_ioppr_check_duplicate_iop_order(&modules, history_list);
1903 reset_list = 1;
1904 }
1905 }
1906 else
1907 {
1908 mod->iop_order += 1.0;
1909 }
1910 }
1911 else if(!mod_prev->enabled && _ioppr_search_history_by_module(history_list, mod_prev) == NULL)
1912 {
1913 can_move = 1;
1914
1915 GList *modules1 = g_list_previous(modules);
1916 if(modules1) modules1 = g_list_previous(modules1);
1917 if(modules1)
1918 {
1919 dt_iop_module_t *mod_next = (dt_iop_module_t *)(modules1->data);
1920 if(mod_prev->iop_order != mod_next->iop_order)
1921 {
1922 mod_prev->iop_order -= (mod_prev->iop_order - mod_next->iop_order) / 2.0;
1923 }
1924 else
1925 {
1926 can_move = 0;
1927 fprintf(stderr,
1928 "[dt_ioppr_check_duplicate_iop_order 1] modules %s %s(%d) and %s %s(%d) have the same iop_order\n",
1929 mod_prev->op, mod_prev->multi_name, mod_prev->iop_order, mod->op, mod->multi_name, mod->iop_order);
1930 }
1931 }
1932 else
1933 {
1934 mod_prev->iop_order -= 0.5;
1935 }
1936 }
1937
1938 if(!can_move)
1939 {
1940 fprintf(stderr,
1941 "[dt_ioppr_check_duplicate_iop_order] modules %s %s(%d) and %s %s(%d) have the same iop_order\n",
1942 mod_prev->op, mod_prev->multi_name, mod_prev->iop_order, mod->op, mod->multi_name, mod->iop_order);
1943 }
1944 }
1945
1946 if(reset_list)
1947 {
1948 modules = iop_list;
1949 if(modules)
1950 {
1951 mod_prev = (dt_iop_module_t *)(modules->data);
1952 modules = g_list_next(modules);
1953 }
1954 }
1955 else
1956 {
1957 mod_prev = mod;
1958 modules = g_list_next(modules);
1959 }
1960 }
1961
1962 *_iop_list = iop_list;
1963}
1964
1965// check if all so modules on iop_list have a iop_order defined in iop_order_list
1966int dt_ioppr_check_so_iop_order(GList *iop_list, GList *iop_order_list)
1967{
1968 int iop_order_missing = 0;
1969
1970 // check if all the modules have their iop_order assigned
1971 for(const GList *modules = iop_list; modules; modules = g_list_next(modules))
1972 {
1973 const dt_iop_module_so_t *const restrict mod = (dt_iop_module_so_t *)(modules->data);
1974 const dt_iop_order_entry_t *const restrict entry =
1975 dt_ioppr_get_iop_order_entry(iop_order_list, mod->op, 0); // mod->multi_priority);
1976 if(IS_NULL_PTR(entry) && !dt_deprecated(mod->op))
1977 {
1978 iop_order_missing = 1;
1979 fprintf(stderr, "[dt_ioppr_check_so_iop_order] missing iop_order for module %s\n", mod->op);
1980 }
1981 }
1982
1983 return iop_order_missing;
1984}
1985
1993static void *_dup_iop_order_entry(const void *src, gpointer data)
1994{
1995 const dt_iop_order_entry_t *const restrict scr_entry = (dt_iop_order_entry_t *)src;
1996 dt_iop_order_entry_t *new_entry = malloc(sizeof(dt_iop_order_entry_t));
1997 memcpy(new_entry, scr_entry, sizeof(dt_iop_order_entry_t));
1998 return (void *)new_entry;
1999}
2000
2001// returns a duplicate of iop_order_list
2002GList *dt_ioppr_iop_order_copy_deep(GList *iop_order_list)
2003{
2004 return (GList *)g_list_copy_deep(iop_order_list, _dup_iop_order_entry, NULL);
2005}
2006
2007// helper to sort a GList of dt_iop_module_t by iop_order
2008gint dt_sort_iop_by_order(gconstpointer a, gconstpointer b)
2009{
2010 const dt_iop_module_t *const restrict am = (const dt_iop_module_t *)a;
2011 const dt_iop_module_t *const restrict bm = (const dt_iop_module_t *)b;
2012 if(am->iop_order > bm->iop_order) return 1;
2013 if(am->iop_order < bm->iop_order) return -1;
2014 return 0;
2015}
2016
2017// if module can be placed before than module_next on the pipe
2018// it returns the new iop_order
2019// if it cannot be placed it returns -1.0
2020// this assumes that the order is always positive
2021gboolean dt_ioppr_check_can_move_before_iop(GList *iop_list, dt_iop_module_t *module, dt_iop_module_t *module_next)
2022{
2023 // we should't be here if the next module is using a raster mask and and our module is that raster mask source
2024 if(module_next->raster_mask.sink.source == module)
2025 return FALSE;
2026
2027 gboolean can_move = FALSE;
2028
2029 // module is before on the pipe
2030 // move it up
2031 if(module->iop_order < module_next->iop_order)
2032 {
2033 // let's first search for module
2034 GList *modules = iop_list;
2035 for(; modules; modules = g_list_next(modules))
2036 {
2037 const dt_iop_module_t *const restrict mod = (dt_iop_module_t *)modules->data;
2038 if(mod == module) break;
2039 }
2040
2041 // we found the module
2042 if(modules)
2043 {
2044 dt_iop_module_t *mod1 = NULL;
2045 dt_iop_module_t *mod2 = NULL;
2046
2047 // now search for module_next and the one previous to that, so iop_order can be calculated
2048 // also check the rules
2049 for(modules = g_list_next(modules); modules; modules = g_list_next(modules))
2050 {
2051 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
2052
2053 // if we reach module_next everything is OK
2054 if(mod == module_next)
2055 {
2056 mod2 = mod;
2057 break;
2058 }
2059
2060 // moving a module that is the source for a raster mask ABOVE the module using it is forbidden
2061 if(mod->raster_mask.sink.source == module)
2062 break;
2063
2064 // is there a rule about swapping this two?
2065 int rule_found = 0;
2066 for(const GList *rules = darktable.iop_order_rules; rules; rules = g_list_next(rules))
2067 {
2068 const dt_iop_order_rule_t *const restrict rule = (dt_iop_order_rule_t *)rules->data;
2069
2070 if(strcmp(module->op, rule->op_prev) == 0 && strcmp(mod->op, rule->op_next) == 0)
2071 {
2072 rule_found = 1;
2073 break;
2074 }
2075 }
2076 if(rule_found) break;
2077
2078 mod1 = mod;
2079 }
2080
2081 // we reach the module_next module
2082 if(mod2)
2083 {
2084 // this is already the previous module!
2085 if(module == mod1)
2086 {
2087 ;
2088 }
2089 else if(mod1->iop_order == mod2->iop_order)
2090 {
2091 fprintf(stderr, "[dt_ioppr_get_iop_order_before_iop] %s %s(%d) and %s %s(%d) have the same iop_order\n",
2092 mod1->op, mod1->multi_name, mod1->iop_order, mod2->op, mod2->multi_name, mod2->iop_order);
2093 }
2094 else
2095 {
2096 can_move = TRUE;
2097 }
2098 }
2099 }
2100 else
2101 fprintf(stderr, "[dt_ioppr_get_iop_order_before_iop] can't find module %s %s\n", module->op, module->multi_name);
2102 }
2103 // module is next on the pipe
2104 // move it down
2105 else if(module->iop_order > module_next->iop_order)
2106 {
2107 // let's first search for module
2108 GList *modules = g_list_last(iop_list);
2109 for(; modules; modules = g_list_previous(modules))
2110 {
2111 const dt_iop_module_t *const restrict mod = (dt_iop_module_t *)modules->data;
2112 if(mod == module) break;
2113 }
2114
2115 // we found the module
2116 if(modules)
2117 {
2118 dt_iop_module_t *mod1 = NULL;
2119 dt_iop_module_t *mod2 = NULL;
2120
2121 // now search for module_next and the one next to that, so iop_order can be calculated
2122 // also check the rules
2123 for(modules = g_list_previous(modules); modules; modules = g_list_previous(modules))
2124 {
2125 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
2126
2127 // we reach the module next to module_next, everything is OK
2128 if(!IS_NULL_PTR(mod2))
2129 {
2130 mod1 = mod;
2131 break;
2132 }
2133
2134 // moving a module using a raster mask BELOW its raster source module is forbidden
2135 if(module->raster_mask.sink.source == mod)
2136 break;
2137
2138 // is there a rule about swapping this two?
2139 int rule_found = 0;
2140 for(const GList *rules = darktable.iop_order_rules; rules; rules = g_list_next(rules))
2141 {
2142 const dt_iop_order_rule_t *const restrict rule = (dt_iop_order_rule_t *)rules->data;
2143
2144 if(strcmp(mod->op, rule->op_prev) == 0 && strcmp(module->op, rule->op_next) == 0)
2145 {
2146 rule_found = 1;
2147 break;
2148 }
2149 }
2150 if(rule_found) break;
2151
2152 if(mod == module_next) mod2 = mod;
2153 }
2154
2155 // we reach the module_next module
2156 if(mod1)
2157 {
2158 // this is already the previous module!
2159 if(module == mod2)
2160 {
2161 ;
2162 }
2163 else if(mod1->iop_order == mod2->iop_order)
2164 {
2165 fprintf(stderr, "[dt_ioppr_get_iop_order_before_iop] %s %s(%d) and %s %s(%d) have the same iop_order\n",
2166 mod1->op, mod1->multi_name, mod1->iop_order, mod2->op, mod2->multi_name, mod2->iop_order);
2167 }
2168 else
2169 {
2170 can_move = TRUE;
2171 }
2172 }
2173 }
2174 else
2175 fprintf(stderr, "[dt_ioppr_get_iop_order_before_iop] can't find module %s %s\n", module->op, module->multi_name);
2176 }
2177 else
2178 {
2179 fprintf(stderr, "[dt_ioppr_get_iop_order_before_iop] modules %s %s(%d) and %s %s(%d) have the same iop_order\n",
2180 module->op, module->multi_name, module->iop_order, module_next->op, module_next->multi_name, module_next->iop_order);
2181 }
2182
2183 return can_move;
2184}
2185
2186// if module can be placed after than module_prev on the pipe
2187// it returns the new iop_order
2188// if it cannot be placed it returns -1.0
2189// this assumes that the order is always positive
2190gboolean dt_ioppr_check_can_move_after_iop(GList *iop_list, dt_iop_module_t *module, dt_iop_module_t *module_prev)
2191{
2192 // we shouldn't be here if the previous module is the a raster mask's source and our module is using it
2193 if(module->raster_mask.sink.source == module_prev)
2194 return FALSE;
2195
2196 gboolean can_move = FALSE;
2197
2198 // moving after module_prev is the same as moving before the very next one after module_prev
2199 dt_iop_module_t *module_next = NULL;
2200
2201 for(const GList *modules = g_list_last(iop_list); modules; modules = g_list_previous(modules))
2202 {
2203 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
2204 if(mod == module_prev) break;
2205
2206 module_next = mod;
2207 }
2208 if(IS_NULL_PTR(module_next))
2209 {
2210 fprintf(
2211 stderr,
2212 "[dt_ioppr_get_iop_order_after_iop] can't find module previous to %s %s(%d) while moving %s %s(%d) after it\n",
2213 module_prev->op, module_prev->multi_name, module_prev->iop_order, module->op, module->multi_name,
2214 module->iop_order);
2215 }
2216 else
2217 can_move = dt_ioppr_check_can_move_before_iop(iop_list, module, module_next);
2218
2219 return can_move;
2220}
2221
2222// changes the module->iop_order so it comes before in the pipe than module_next
2223// sort dev->iop to reflect the changes
2224// return 1 if iop_order is changed, 0 otherwise
2225gboolean dt_ioppr_move_iop_before(struct dt_develop_t *dev, dt_iop_module_t *module, dt_iop_module_t *module_next)
2226{
2227 GList *next = dt_ioppr_get_iop_order_link(dev->iop_order_list, module_next->op, module_next->multi_priority);
2228 GList *current = dt_ioppr_get_iop_order_link(dev->iop_order_list, module->op, module->multi_priority);
2229
2230 if(IS_NULL_PTR(next)) return FALSE;
2231
2232 if(IS_NULL_PTR(current))
2233 {
2234 // Module not in iop_order_list, create entry
2236 g_strlcpy(entry->operation, module->op, sizeof(entry->operation));
2237 entry->instance = module->multi_priority;
2238 entry->o.iop_order = 0; // will be reset later
2239 current = g_list_alloc();
2240 current->data = entry;
2241 }
2242
2243 dev->iop_order_list = g_list_remove_link(dev->iop_order_list, current);
2244 dev->iop_order_list = g_list_insert_before(dev->iop_order_list, next, current->data);
2245
2246 g_list_free(current);
2247 current = NULL;
2248
2250
2251 return TRUE;
2252}
2253
2254// changes the module->iop_order so it comes after in the pipe than module_prev
2255// sort dev->iop to reflect the changes
2256// return 1 if iop_order is changed, 0 otherwise
2257gboolean dt_ioppr_move_iop_after(struct dt_develop_t *dev, dt_iop_module_t *module, dt_iop_module_t *module_prev)
2258{
2259 GList *prev = dt_ioppr_get_iop_order_link(dev->iop_order_list, module_prev->op, module_prev->multi_priority);
2260 GList *current = dt_ioppr_get_iop_order_link(dev->iop_order_list, module->op, module->multi_priority);
2261
2262 if(IS_NULL_PTR(prev)) return FALSE;
2263
2264 if(IS_NULL_PTR(current))
2265 {
2266 // Module not in iop_order_list, create entry
2268 g_strlcpy(entry->operation, module->op, sizeof(entry->operation));
2269 entry->instance = module->multi_priority;
2270 entry->o.iop_order = 0; // will be reset later
2271 current = g_list_alloc();
2272 current->data = entry;
2273 }
2274
2275 dev->iop_order_list = g_list_remove_link(dev->iop_order_list, current);
2276
2277 // we want insert after => so insert before the next item
2278 GList *next = g_list_next(prev);
2279 if(prev)
2280 dev->iop_order_list = g_list_insert_before(dev->iop_order_list, next, current->data);
2281 else
2282 dev->iop_order_list = g_list_append(dev->iop_order_list, current->data);
2283
2284 g_list_free(current);
2285 current = NULL;
2286
2288
2289 return TRUE;
2290}
2291
2301static void _ioppr_check_rules(GList *iop_list, const int32_t imgid, const char *msg)
2302{
2303 // for each module check if it doesn't break a rule
2304 for(const GList *modules = iop_list; modules; modules = g_list_next(modules))
2305 {
2306 const dt_iop_module_t *const restrict mod = (dt_iop_module_t *)modules->data;
2307 if(mod->iop_order == INT_MAX)
2308 {
2309 continue;
2310 }
2311
2312 // we have a module, now check each rule
2313 for(const GList *rules = darktable.iop_order_rules; rules; rules = g_list_next(rules))
2314 {
2315 const dt_iop_order_rule_t *const restrict rule = (dt_iop_order_rule_t *)rules->data;
2316
2317 // mod must be before rule->op_next
2318 if(strcmp(mod->op, rule->op_prev) == 0)
2319 {
2320 // check if there's a rule->op_next module before mod
2321 for(const GList *modules_prev = g_list_previous(modules);
2322 modules_prev;
2323 modules_prev = g_list_previous(modules_prev))
2324 {
2325 const dt_iop_module_t *const restrict mod_prev = (dt_iop_module_t *)modules_prev->data;
2326
2327 if(strcmp(mod_prev->op, rule->op_next) == 0)
2328 {
2329 fprintf(stderr, "[_ioppr_check_rules] found rule %s %s module %s %s(%d) is after %s %s(%d) image %i (%s)\n",
2330 rule->op_prev, rule->op_next, mod->op, mod->multi_name, mod->iop_order, mod_prev->op,
2331 mod_prev->multi_name, mod_prev->iop_order, imgid, msg);
2332 }
2333 }
2334 }
2335 // mod must be after rule->op_prev
2336 else if(strcmp(mod->op, rule->op_next) == 0)
2337 {
2338 // check if there's a rule->op_prev module after mod
2339 for(const GList *modules_next = g_list_next(modules); modules_next; modules_next = g_list_next(modules_next))
2340 {
2341 const dt_iop_module_t *const restrict mod_next = (dt_iop_module_t *)modules_next->data;
2342
2343 if(strcmp(mod_next->op, rule->op_prev) == 0)
2344 {
2345 fprintf(stderr, "[_ioppr_check_rules] found rule %s %s module %s %s(%d) is before %s %s(%d) image %i (%s)\n",
2346 rule->op_prev, rule->op_next, mod->op, mod->multi_name, mod->iop_order, mod_next->op,
2347 mod_next->multi_name, mod_next->iop_order, imgid, msg);
2348 }
2349 }
2350 }
2351 }
2352 }
2353}
2354
2356{
2357 const char *operation = module->op;
2358 const int32_t instance = module->multi_priority;
2359
2361
2362 g_strlcpy(entry->operation, operation, sizeof(entry->operation));
2363 entry->instance = instance;
2364 entry->o.iop_order = 0;
2365
2366 GList *place = NULL;
2367
2368 int max_instance = -1;
2369
2370 for(GList *l = dev->iop_order_list; l; l = g_list_next(l))
2371 {
2372 const dt_iop_order_entry_t *const restrict e = (dt_iop_order_entry_t *)l->data;
2373 if(!strcmp(e->operation, operation) && e->instance > max_instance)
2374 {
2375 place = l;
2376 max_instance = e->instance;
2377 }
2378 }
2379
2380 dev->iop_order_list = g_list_insert_before(dev->iop_order_list, place, entry);
2381}
2382
2383int dt_ioppr_check_iop_order(dt_develop_t *dev, const int32_t imgid, const char *msg)
2384{
2385 int iop_order_ok = 1;
2386
2387 // check if gamma is the last iop
2388 {
2389 GList *modules;
2390 for(modules = g_list_last(dev->iop); modules; modules = g_list_previous(dev->iop))
2391 {
2392 const dt_iop_module_t *const restrict mod = (dt_iop_module_t *)modules->data;
2393 if(mod->iop_order != INT_MAX)
2394 break;
2395 }
2396 if(modules)
2397 {
2398 const dt_iop_module_t *const restrict mod = (dt_iop_module_t *)modules->data;
2399
2400 if(strcmp(mod->op, "gamma") != 0)
2401 {
2402 iop_order_ok = 0;
2403 fprintf(stderr, "[dt_ioppr_check_iop_order] gamma is not the last iop, last is %s %s(%d) image %i (%s)\n",
2404 mod->op, mod->multi_name, mod->iop_order,imgid, msg);
2405 }
2406 }
2407 else
2408 {
2409 // fprintf(stderr, "[dt_ioppr_check_iop_order] dev->iop is empty image %i (%s)\n",imgid, msg);
2410 }
2411 }
2412
2413 // some other checks
2414 {
2415 for(const GList *modules = g_list_last(dev->iop); modules; modules = g_list_previous(dev->iop))
2416 {
2417 const dt_iop_module_t *const restrict mod = (dt_iop_module_t *)modules->data;
2418 if(!mod->default_enabled && mod->iop_order != INT_MAX)
2419 {
2420 if(mod->enabled)
2421 {
2422 iop_order_ok = 0;
2423 fprintf(stderr, "[dt_ioppr_check_iop_order] module not used but enabled!! %s %s(%d) image %i (%s)\n",
2424 mod->op, mod->multi_name, mod->iop_order,imgid, msg);
2425 }
2426 if(mod->multi_priority == 0)
2427 {
2428 iop_order_ok = 0;
2429 fprintf(stderr, "[dt_ioppr_check_iop_order] base module set as not used %s %s(%d) image %i (%s)\n",
2430 mod->op, mod->multi_name, mod->iop_order,imgid, msg);
2431 }
2432 }
2433 }
2434 }
2435
2436 // check if there's duplicate or out-of-order iop_order
2437 {
2438 dt_iop_module_t *mod_prev = NULL;
2439 for(const GList *modules = g_list_first(dev->iop); modules; modules = g_list_next(modules))
2440 {
2441 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
2442 if(mod->iop_order != INT_MAX)
2443 {
2444 if(mod_prev)
2445 {
2446 if(mod->iop_order < mod_prev->iop_order)
2447 {
2448 iop_order_ok = 0;
2449 fprintf(stderr,
2450 "[dt_ioppr_check_iop_order] module %s %s(%d) should be after %s %s(%d) image %i (%s)\n",
2451 mod->op, mod->multi_name, mod->iop_order, mod_prev->op, mod_prev->multi_name,
2452 mod_prev->iop_order, imgid, msg);
2453 }
2454 else if(mod->iop_order == mod_prev->iop_order)
2455 {
2456 iop_order_ok = 0;
2457 fprintf(
2458 stderr,
2459 "[dt_ioppr_check_iop_order] module %s %s(%i)(%d) and %s %s(%i)(%d) have the same order image %i (%s)\n",
2460 mod->op, mod->multi_name, mod->multi_priority, mod->iop_order, mod_prev->op,
2461 mod_prev->multi_name, mod_prev->multi_priority, mod_prev->iop_order, imgid, msg);
2462 }
2463 }
2464 }
2465 mod_prev = mod;
2466 }
2467 }
2468
2469 _ioppr_check_rules(dev->iop, imgid, msg);
2470
2471 for(const GList *history = dev->history; history; history = g_list_next(history))
2472 {
2473 const dt_dev_history_item_t *const restrict hist = (dt_dev_history_item_t *)(history->data);
2474
2475 if(hist->iop_order == INT_MAX)
2476 {
2477 if(hist->enabled)
2478 {
2479 iop_order_ok = 0;
2480 fprintf(stderr, "[dt_ioppr_check_iop_order] history module not used but enabled!! %s %s(%d) image %i (%s)\n",
2481 hist->op_name, hist->multi_name, hist->iop_order, imgid, msg);
2482 }
2483 if(hist->multi_priority == 0)
2484 {
2485 iop_order_ok = 0;
2486 fprintf(stderr, "[dt_ioppr_check_iop_order] history base module set as not used %s %s(%d) image %i (%s)\n",
2487 hist->op_name, hist->multi_name, hist->iop_order, imgid, msg);
2488 }
2489 }
2490 }
2491
2492 dt_print(DT_DEBUG_PARAMS, "[dt_ioppr_check_iop_order] IOP order passed (called from %s)\n", msg);
2493
2494 return iop_order_ok;
2495}
2496
2497void *dt_ioppr_serialize_iop_order_list(GList *iop_order_list, size_t *size)
2498{
2499 g_return_val_if_fail(!IS_NULL_PTR(iop_order_list), NULL);
2500 g_return_val_if_fail(!IS_NULL_PTR(size), NULL);
2501 // compute size of all modules
2502 *size = 0;
2503
2504 for(const GList *l = iop_order_list; l; l = g_list_next(l))
2505 {
2506 const dt_iop_order_entry_t *const restrict entry = (dt_iop_order_entry_t *)l->data;
2507 *size += strlen(entry->operation) + sizeof(int32_t) * 2;
2508 }
2509
2510 if(*size == 0)
2511 return NULL;
2512
2513 // allocate the parameter buffer
2514 char *params = (char *)malloc(*size);
2515
2516 // set set preset iop-order version
2517 int pos = 0;
2518
2519 for(const GList *l = iop_order_list; l; l = g_list_next(l))
2520 {
2521 const dt_iop_order_entry_t *const restrict entry = (dt_iop_order_entry_t *)l->data;
2522 // write the len of the module name
2523 const int32_t len = strlen(entry->operation);
2524 memcpy(params+pos, &len, sizeof(int32_t));
2525 pos += sizeof(int32_t);
2526
2527 // write the module name
2528 memcpy(params+pos, entry->operation, len);
2529 pos += len;
2530
2531 // write the instance number
2532 memcpy(params+pos, &(entry->instance), sizeof(int32_t));
2533 pos += sizeof(int32_t);
2534 }
2535
2536 return params;
2537}
2538
2539char *dt_ioppr_serialize_text_iop_order_list(GList *iop_order_list)
2540{
2541 GString *text = g_string_new("");
2542
2543 const GList *const last = g_list_last(iop_order_list);
2544 for(const GList *l = iop_order_list; l; l = g_list_next(l))
2545 {
2546 const dt_iop_order_entry_t *const restrict entry = (dt_iop_order_entry_t *)l->data;
2547 gchar buf[64];
2548 snprintf(buf, sizeof(buf), "%s,%d%s", entry->operation, entry->instance, (l == last) ? "" : ",");
2549 g_string_append(text, buf);
2550 }
2551
2552 return g_string_free(text, FALSE);
2553}
2554
2555/* this sanity check routine is used to correct wrong iop-list that
2556 * could have been stored while some bugs were present in
2557 * dartkable. There was a window around Sep 2019 where such issue
2558 * existed and some xmp may have been corrupt at this time making dt
2559 * crash while reimporting using the xmp.
2560 *
2561 * One common case seems that the list does not end with gamma.
2562*/
2563
2572static gboolean _ioppr_sanity_check_iop_order(GList *list)
2573{
2574 gboolean ok = TRUE;
2575
2576 // First check that first module is basebuffer (even for a jpeg, we
2577 // are speaking of the module ordering not the activated modules.
2578
2579 GList *first = g_list_first(list);
2580 dt_iop_order_entry_t *entry_first = (dt_iop_order_entry_t *)first->data;
2581
2582 ok = ok && (g_strcmp0(entry_first->operation, "basebuffer") == 0);
2583
2584 // Then check that last module is gamma
2585
2586 GList *last = g_list_last(list);
2587 dt_iop_order_entry_t *entry_last = (dt_iop_order_entry_t *)last->data;
2588
2589 ok = ok && (g_strcmp0(entry_last->operation, "gamma") == 0);
2590
2591 return ok;
2592}
2593
2595{
2596 GList *iop_order_list = NULL;
2597
2598 GList *list = dt_util_str_to_glist(",", buf);
2599 for(GList *l = list; l; l = g_list_next(l))
2600 {
2602 entry->o.iop_order = 0;
2603
2604 // first operation name
2605 g_strlcpy(entry->operation, (char *)l->data, sizeof(entry->operation));
2606
2607 // then operation instance
2608 l = g_list_next(l);
2609 if(IS_NULL_PTR(l)) goto error;
2610
2611 const char *data = (char *)l->data;
2612 int inst = 0;
2613 sscanf(data, "%d", &inst);
2614 entry->instance = inst;
2615
2616 // append to the list
2617 iop_order_list = g_list_prepend(iop_order_list, entry);
2618 }
2619 iop_order_list = g_list_reverse(iop_order_list); // list was built in reverse order, so un-reverse it
2620
2621 g_list_free_full(list, dt_free_gpointer);
2622 list = NULL;
2623
2624 _ioppr_reset_iop_order(iop_order_list);
2625
2626 // Remove sanity check from here; it's now handled after migration in dt_ioppr_get_iop_order_list()
2627
2628 return iop_order_list;
2629
2630 error:
2631 g_list_free_full(list, dt_free_gpointer);
2632 list = NULL;
2633 g_list_free_full(iop_order_list, dt_free_gpointer);
2634 iop_order_list = NULL;
2635 return NULL;
2636}
2637
2638GList *dt_ioppr_deserialize_iop_order_list(const char *buf, size_t size)
2639{
2640 GList *iop_order_list = NULL;
2641
2642 // parse all modules
2643 while(size)
2644 {
2646
2647 entry->o.iop_order = 0;
2648
2649 // get length of module name
2650 const int32_t len = *(int32_t *)buf;
2651 buf += sizeof(int32_t);
2652
2653 if(len < 0 || len > 20) { dt_free(entry); goto error; }
2654
2655 // set module name
2656 memcpy(entry->operation, buf, len);
2657 *(entry->operation + len) = '\0';
2658 buf += len;
2659
2660 // get the instance number
2661 entry->instance = *(int32_t *)buf;
2662 buf += sizeof(int32_t);
2663
2664 if(entry->instance < 0 || entry->instance > 1000) { dt_free(entry); goto error; }
2665
2666 // append to the list
2667 iop_order_list = g_list_prepend(iop_order_list, entry);
2668
2669 size -= (2 * sizeof(int32_t) + len);
2670 }
2671 iop_order_list = g_list_reverse(iop_order_list); // list was built in reverse order, so un-reverse it
2672
2673 _ioppr_reset_iop_order(iop_order_list);
2674
2675 return iop_order_list;
2676
2677 error:
2678 g_list_free_full(iop_order_list, dt_free_gpointer);
2679 iop_order_list = NULL;
2680 return NULL;
2681}
2682
2683#undef DT_IOP_ORDER_INFO
2684// clang-format off
2685// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
2686// vim: shiftwidth=2 expandtab tabstop=2 cindent
2687// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
2688// 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:183
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1600
@ DT_DEBUG_PARAMS
Definition darktable.h:758
static void dt_free_gpointer(gpointer ptr)
Definition darktable.h:485
#define dt_free(ptr)
Definition darktable.h:478
#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:293
static gboolean g_list_shorter_than(const GList *list, unsigned len)
Definition darktable.h:1001
sqlite3 * dt_database_get(const dt_database_t *db)
Definition database.c:3653
#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:3178
dt_iop_module_t * dt_iop_get_module_by_op_priority(GList *modules, const char *operation, const int multi_priority)
Definition imageop.c:3160
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:1865
static GList * dt_ioppr_extract_multi_instances_list(GList *iop_order_list)
Extract all order entries that have multiple instances.
Definition iop_order.c:1413
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:2301
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:2572
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:1441
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:2497
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:1993
gint dt_sort_iop_by_order(gconstpointer a, gconstpointer b)
Compare two module instances by iop_order for sorting.
Definition iop_order.c:2008
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:1383
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:1836
static int _count_entries_operation(GList *e_list, const char *operation)
Count order-list entries matching an operation name.
Definition iop_order.c:1593
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:1300
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:2021
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:2190
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:2257
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:1504
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:2002
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:867
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:1632
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:1966
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:2225
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:1753
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:1331
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:2539
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:2594
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:2355
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:1790
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:856
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:1613
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:2638
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:2383
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:1559
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:1657
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:1392
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:829
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:807
GList * iop_order_rules
Definition darktable.h:791
struct dt_image_cache_t * image_cache
Definition darktable.h:805
struct dt_develop_t * develop
Definition darktable.h:798
GList * iop_order_list
Definition develop.h:291
dt_image_t image_storage
Definition develop.h:259
GList * iop
Definition develop.h:285
GList * history
Definition develop.h:275
int32_t id
Definition image.h:319
char multi_name[128]
Definition imageop.h:400
GModule *dt_dev_operation_t op
Definition imageop.h:286
struct dt_iop_module_t::@29 raster_mask
gboolean enabled
Definition imageop.h:335
struct dt_iop_module_t::@29::@30 source
struct dt_iop_module_t::@29::@31 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
GHashTable * entries
Definition supervisor.c:116
#define MAX(a, b)
Definition thinplate.c:29
GList * dt_util_str_to_glist(const gchar *separator, const gchar *text)
Definition utility.c:830