39
39
]] local dt = require " darktable"
40
40
local du = require " lib/dtutils"
41
41
local df = require " lib/dtutils.file"
42
+ local ds = require " lib/dtutils.string"
42
43
local log = require " lib/dtutils.log"
43
44
local dtsys = require " lib/dtutils.system"
44
45
local dd = require " lib/dtutils.debug"
@@ -65,8 +66,9 @@ local GUI = {
65
66
encoding_settings_box = {},
66
67
output_settings_label = {},
67
68
output_settings_box = {},
68
- use_original_directory = {},
69
- output_directory_widget = {},
69
+ output_filepath_label = {},
70
+ output_filepath_widget = {},
71
+ overwrite_on_conflict = {},
70
72
copy_exif = {},
71
73
import_to_darktable = {},
72
74
min_content_boost = {},
@@ -110,6 +112,12 @@ local SELECTION_TYPE_GROUP_BY_FNAME = 2
110
112
local DT_COLORSPACE_PQ_P3 = 24
111
113
local DT_COLORSPACE_DISPLAY_P3 = 26
112
114
115
+ -- 1-based position of a colorspace in export profile combobox.
116
+ local COLORSPACE_TO_GUI_ACTION = {
117
+ [DT_COLORSPACE_PQ_P3 ] = 9 ,
118
+ [DT_COLORSPACE_DISPLAY_P3 ] = 11
119
+ }
120
+
113
121
local function generate_metadata_file (settings )
114
122
local metadata_file_fmt = [[ --maxContentBoost %f
115
123
--minContentBoost %f
135
143
local function save_preferences ()
136
144
dt .preferences .write (namespace , " encoding_variant" , " integer" , GUI .optionwidgets .encoding_variant_combo .selected )
137
145
dt .preferences .write (namespace , " selection_type" , " integer" , GUI .optionwidgets .selection_type_combo .selected )
138
- dt .preferences .write (namespace , " use_original_directory" , " bool" , GUI .optionwidgets .use_original_directory .value )
139
- if GUI .optionwidgets .output_directory_widget .value then
140
- dt .preferences .write (namespace , " output_directory" , " string" , GUI .optionwidgets .output_directory_widget .value )
141
- end
146
+ dt .preferences .write (namespace , " output_filepath_pattern" , " string" , GUI .optionwidgets .output_filepath_widget .text )
147
+ dt .preferences .write (namespace , " overwrite_on_conflict" , " bool" , GUI .optionwidgets .overwrite_on_conflict .value )
142
148
dt .preferences .write (namespace , " import_to_darktable" , " bool" , GUI .optionwidgets .import_to_darktable .value )
143
149
dt .preferences .write (namespace , " copy_exif" , " bool" , GUI .optionwidgets .copy_exif .value )
144
150
if GUI .optionwidgets .min_content_boost .value then
@@ -151,7 +157,7 @@ local function save_preferences()
151
157
dt .preferences .write (namespace , " gainmap_downsampling" , " integer" ,
152
158
GUI .optionwidgets .gainmap_downsampling_widget .value )
153
159
dt .preferences .write (namespace , " target_display_peak_nits" , " integer" ,
154
- (GUI .optionwidgets .target_display_peak_nits_widget .value + 0.5 )// 1 )
160
+ (GUI .optionwidgets .target_display_peak_nits_widget .value + 0.5 ) // 1 )
155
161
156
162
end
157
163
@@ -169,11 +175,8 @@ local function load_preferences()
169
175
GUI .optionwidgets .selection_type_combo .selected = math.max (
170
176
dt .preferences .read (namespace , " selection_type" , " integer" ), SELECTION_TYPE_ONE_STACK )
171
177
172
- GUI .optionwidgets .output_directory_widget .value = dt .preferences .read (namespace , " output_directory" , " string" )
173
- GUI .optionwidgets .use_original_directory .value = dt .preferences .read (namespace , " use_original_directory" , " bool" )
174
- if not GUI .optionwidgets .output_directory_widget .value then
175
- GUI .optionwidgets .use_original_directory .value = true
176
- end
178
+ GUI .optionwidgets .output_filepath_widget .text = dt .preferences .read (namespace , " output_filepath_pattern" , " string" )
179
+ GUI .optionwidgets .overwrite_on_conflict .value = dt .preferences .read (namespace , " overwrite_on_conflict" , " bool" )
177
180
GUI .optionwidgets .import_to_darktable .value = dt .preferences .read (namespace , " import_to_darktable" , " bool" )
178
181
GUI .optionwidgets .copy_exif .value = dt .preferences .read (namespace , " copy_exif" , " bool" )
179
182
GUI .optionwidgets .min_content_boost .value = default_to (dt .preferences .read (namespace , " min_content_boost" , " float" ),
@@ -191,10 +194,25 @@ local function load_preferences()
191
194
dt .preferences .read (namespace , " gainmap_downsampling" , " integer" ), 0 )
192
195
end
193
196
197
+ local function set_profile (colorspace )
198
+ local set_directly = true
199
+
200
+ if set_directly then
201
+ -- New method, with hardcoded export profile values.
202
+ local old = dt .gui .action (" lib/export/profile" , 0 , " selection" , " " , " " ) * - 1
203
+ local new = COLORSPACE_TO_GUI_ACTION [colorspace ] or colorspace
204
+ log .msg (log .debug , string.format (" %d %d %d %d" , colorspace , new , old , new - old ))
205
+ dt .gui .action (" lib/export/profile" , 0 , " selection" , " next" , new - old )
206
+ return old
207
+ else
208
+ -- Old method, timing-dependent
209
+ return set_combobox (" lib/export/profile" , 0 , " plugins/lighttable/export/icctype" , colorspace )
210
+ end
211
+ end
212
+
194
213
-- Changes the combobox selection blindly until a paired config value is set.
195
214
-- Workaround for https://github.com/darktable-org/lua-scripts/issues/522
196
215
local function set_combobox (path , instance , config_name , new_config_value )
197
-
198
216
local pref = dt .preferences .read (" darktable" , config_name , " integer" )
199
217
if pref == new_config_value then
200
218
return new_config_value
@@ -223,8 +241,8 @@ local function assert_settings_correct(encoding_variant)
223
241
exiftool = df .check_if_bin_exists (" exiftool" ),
224
242
ffmpeg = df .check_if_bin_exists (" ffmpeg" )
225
243
},
226
- output = GUI .optionwidgets .output_directory_widget .value ,
227
- use_original_dir = GUI .optionwidgets .use_original_directory . value ,
244
+ overwrite_on_conflict = GUI .optionwidgets .overwrite_on_conflict .value ,
245
+ output_filepath_pattern = GUI .optionwidgets .output_filepath_widget . text ,
228
246
import_to_darktable = GUI .optionwidgets .import_to_darktable .value ,
229
247
copy_exif = GUI .optionwidgets .copy_exif .value ,
230
248
metadata = {
@@ -234,16 +252,13 @@ local function assert_settings_correct(encoding_variant)
234
252
hdr_capacity_max = GUI .optionwidgets .hdr_capacity_max .value
235
253
},
236
254
quality = GUI .optionwidgets .quality_widget .value ,
237
- target_display_peak_nits = (GUI .optionwidgets .target_display_peak_nits_widget .value + 0.5 )// 1 ,
255
+ target_display_peak_nits = (GUI .optionwidgets .target_display_peak_nits_widget .value + 0.5 ) // 1 ,
238
256
downsample = 2 ^ GUI .optionwidgets .gainmap_downsampling_widget .value ,
239
257
tmpdir = dt .configuration .tmp_dir ,
240
- skip_cleanup = false -- keep temporary files around, for debugging.
258
+ skip_cleanup = false , -- keep temporary files around, for debugging.
259
+ force_export = true -- if false, will copy source files instead of exporting if the file extension matches the format expectation.
241
260
}
242
261
243
- if not settings .use_original_dir and (not settings .output or not df .check_if_file_exists (settings .output )) then
244
- table.insert (errors , string.format (_ (" output directory (%s) not found" ), settings .output ))
245
- end
246
-
247
262
for k , v in pairs (settings .bin ) do
248
263
if not v then
249
264
table.insert (errors , string.format (_ (" %s binary not found" ), k ))
@@ -387,10 +402,11 @@ local function generate_ultrahdr(encoding_variant, images, settings, step, total
387
402
end
388
403
389
404
function copy_or_export (src_image , dest , format , colorspace , props )
390
- if df .get_filetype (src_image .filename ) == df .get_filetype (dest ) and not src_image .is_altered then
405
+ if not settings .force_export and df .get_filetype (src_image .filename ) == df .get_filetype (dest ) and
406
+ not src_image .is_altered then
391
407
return df .file_copy (src_image .path .. PS .. src_image .filename , dest )
392
408
else
393
- local prev = set_combobox ( " lib/export/profile " , 0 , " plugins/lighttable/export/icctype " , colorspace )
409
+ local prev = set_profile ( colorspace )
394
410
if not prev then
395
411
return false
396
412
end
@@ -405,7 +421,7 @@ local function generate_ultrahdr(encoding_variant, images, settings, step, total
405
421
end
406
422
407
423
if prev then
408
- set_combobox ( " lib/export/profile " , 0 , " plugins/lighttable/export/icctype " , prev )
424
+ set_profile ( prev )
409
425
end
410
426
return ok
411
427
end
@@ -655,8 +671,10 @@ local function generate_ultrahdr(encoding_variant, images, settings, step, total
655
671
update_job_progress ()
656
672
end
657
673
658
- local output_dir = settings .use_original_dir and best_source_image .path or settings .output
659
- local output_file = df .create_unique_filename (output_dir .. PS .. df .get_filename (uhdr ))
674
+ local output_file = ds .substitute (best_source_image , step + 1 , settings .output_filepath_pattern ) .. " .jpg"
675
+ if not settings .overwrite_on_conflict then
676
+ output_file = df .create_unique_filename (output_file )
677
+ end
660
678
ok = df .file_move (uhdr , output_file )
661
679
if not ok then
662
680
table.insert (errors , string.format (_ (" Error generating UltraHDR for %s" ), best_source_image .filename ))
@@ -733,17 +751,20 @@ GUI.optionwidgets.output_settings_label = dt.new_widget("section_label") {
733
751
label = _ (" output" )
734
752
}
735
753
736
- GUI .optionwidgets .output_directory_widget = dt .new_widget (" file_chooser_button " ) {
737
- title = _ (" select directory to write UltraHDR image files to " ),
738
- is_directory = true
754
+ GUI .optionwidgets .output_filepath_label = dt .new_widget (" label " ) {
755
+ label = _ (" file path pattern " ),
756
+ tooltip = ds . get_substitution_tooltip ()
739
757
}
740
758
741
- GUI .optionwidgets .use_original_directory = dt .new_widget (" check_button" ) {
742
- label = _ (" export to original directory" ),
743
- tooltip = _ (" Write UltraHDR images to the same directory as their original images" ),
744
- clicked_callback = function (self )
745
- GUI .optionwidgets .output_directory_widget .sensitive = not self .value
746
- end
759
+ GUI .optionwidgets .output_filepath_widget = dt .new_widget (" entry" ) {
760
+ tooltip = ds .get_substitution_tooltip (),
761
+ placeholder = _ (" e.g. $(FILE_FOLDER)/$(FILE_NAME)_ultrahdr" )
762
+ }
763
+
764
+ GUI .optionwidgets .overwrite_on_conflict = dt .new_widget (" check_button" ) {
765
+ label = _ (" overwrite if exists" ),
766
+ tooltip = _ (
767
+ " If the output file already exists, overwrite it. If unchecked, a unique filename will be created instead." )
747
768
}
748
769
749
770
GUI .optionwidgets .import_to_darktable = dt .new_widget (" check_button" ) {
@@ -759,8 +780,9 @@ GUI.optionwidgets.copy_exif = dt.new_widget("check_button") {
759
780
GUI .optionwidgets .output_settings_box = dt .new_widget (" box" ) {
760
781
orientation = " vertical" ,
761
782
GUI .optionwidgets .output_settings_label ,
762
- GUI .optionwidgets .use_original_directory ,
763
- GUI .optionwidgets .output_directory_widget ,
783
+ GUI .optionwidgets .output_filepath_label ,
784
+ GUI .optionwidgets .output_filepath_widget ,
785
+ GUI .optionwidgets .overwrite_on_conflict ,
764
786
GUI .optionwidgets .import_to_darktable ,
765
787
GUI .optionwidgets .copy_exif
766
788
}
@@ -845,7 +867,9 @@ This will determine the method used to generate UltraHDR.
845
867
846
868
By default, the first image in a stack is treated as SDR, and the second one is a gain map/HDR.
847
869
You can force the image into a specific stack slot by attaching "hdr" / "gainmap" tags to it.
848
- ]] ), _ (" SDR + gain map" ), _ (" SDR + HDR" ), _ (" SDR only" ), _ (" HDR only" )),
870
+
871
+ For HDR source images, apply a log2(203 nits/10000 nits) = -5.62 EV exposure correction
872
+ before generating UltraHDR.]] ), _ (" SDR + gain map" ), _ (" SDR + HDR" ), _ (" SDR only" ), _ (" HDR only" )),
849
873
selected = 0 ,
850
874
changed_callback = function (self )
851
875
GUI .run .sensitive = self .selected and self .selected > 0
0 commit comments