1 /*
2 This file is part of darktable,
3 Copyright (C) 2014-2021 darktable developers.
4
5 darktable is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 darktable is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include "lua/luastorage.h"
19 #include "common/file_location.h"
20 #include "common/image.h"
21 #include "common/imageio.h"
22 #include "common/imageio_module.h"
23 #include "control/jobs.h"
24 #include "lua/call.h"
25 #include "lua/glist.h"
26 #include "lua/image.h"
27 #include "lua/widget/widget.h"
28 #include <common/darktable.h>
29 #include <stdio.h>
30
31 typedef struct
32 {
33 gboolean data_created;
34 } lua_storage_t;
35
36 typedef struct
37 {
38 char *name;
39 GList *supported_formats;
40 lua_widget widget;
41 } lua_storage_gui_t;
42
push_lua_data(lua_State * L,lua_storage_t * d)43 static void push_lua_data(lua_State*L,lua_storage_t *d)
44 {
45 if(!d->data_created)
46 {
47 lua_pushlightuserdata(L, d);
48 lua_newtable(L);
49 lua_settable(L, LUA_REGISTRYINDEX);
50 d->data_created = true;
51 }
52 lua_pushlightuserdata(L, d);
53 lua_gettable(L, LUA_REGISTRYINDEX);
54 }
name_wrapper(const struct dt_imageio_module_storage_t * self)55 static const char *name_wrapper(const struct dt_imageio_module_storage_t *self)
56 {
57 return ((lua_storage_gui_t *)self->gui_data)->name;
58 }
empty_wrapper(struct dt_imageio_module_storage_t * self)59 static void empty_wrapper(struct dt_imageio_module_storage_t *self){};
default_supported_wrapper(struct dt_imageio_module_storage_t * self,struct dt_imageio_module_format_t * format)60 static int default_supported_wrapper(struct dt_imageio_module_storage_t *self,
61 struct dt_imageio_module_format_t *format)
62 {
63 if(g_list_find(((lua_storage_gui_t *)self->gui_data)->supported_formats, format))
64 {
65 return TRUE;
66 }
67 else
68 {
69 return FALSE;
70 }
71 }
default_dimension_wrapper(struct dt_imageio_module_storage_t * self,dt_imageio_module_data_t * data,uint32_t * width,uint32_t * height)72 static int default_dimension_wrapper(struct dt_imageio_module_storage_t *self, dt_imageio_module_data_t *data,
73 uint32_t *width, uint32_t *height)
74 {
75 return 0;
76 };
77
store_wrapper(struct dt_imageio_module_storage_t * self,struct dt_imageio_module_data_t * self_data,const int imgid,dt_imageio_module_format_t * format,dt_imageio_module_data_t * fdata,const int num,const int total,const gboolean high_quality,const gboolean upscale,const gboolean export_masks,dt_colorspaces_color_profile_type_t icc_type,const gchar * icc_filename,dt_iop_color_intent_t icc_intent,dt_export_metadata_t * metadata)78 static int store_wrapper(struct dt_imageio_module_storage_t *self, struct dt_imageio_module_data_t *self_data,
79 const int imgid, dt_imageio_module_format_t *format, dt_imageio_module_data_t *fdata,
80 const int num, const int total, const gboolean high_quality, const gboolean upscale,
81 const gboolean export_masks, dt_colorspaces_color_profile_type_t icc_type,
82 const gchar *icc_filename, dt_iop_color_intent_t icc_intent, dt_export_metadata_t *metadata)
83 {
84
85 /* construct a temporary file name */
86 char tmpdir[PATH_MAX] = { 0 };
87 gboolean from_cache = FALSE;
88 dt_loc_get_tmp_dir(tmpdir, sizeof(tmpdir));
89
90 char dirname[PATH_MAX] = { 0 };
91 dt_image_full_path(imgid, dirname, sizeof(dirname), &from_cache);
92 dt_image_path_append_version(imgid, dirname, sizeof(dirname));
93 gchar *filename = g_path_get_basename(dirname);
94 gchar *end = g_strrstr(filename, ".") + 1;
95 g_strlcpy(end, format->extension(fdata), sizeof(dirname) - (end - dirname));
96
97 gchar *complete_name = g_build_filename(tmpdir, filename, (char *)NULL);
98
99 if(dt_imageio_export(imgid, complete_name, format, fdata, high_quality, upscale, TRUE, export_masks, icc_type,
100 icc_filename, icc_intent, self, self_data, num, total, metadata) != 0)
101 {
102 fprintf(stderr, "[%s] could not export to file: `%s'!\n", self->name(self), complete_name);
103 g_free(complete_name);
104 g_free(filename);
105 return 1;
106 }
107
108 lua_storage_t *d = (lua_storage_t *)self_data;
109
110 dt_lua_lock();
111 lua_State *L = darktable.lua_state.state;
112
113 push_lua_data(L, d);
114 dt_lua_goto_subtable(L, "files");
115 luaA_push(L, dt_lua_image_t, &(imgid));
116 lua_pushstring(L, complete_name);
117 lua_settable(L, -3);
118 lua_pop(L, 1);
119
120
121
122
123 lua_getfield(L, LUA_REGISTRYINDEX, "dt_lua_storages");
124 lua_getfield(L, -1, self->plugin_name);
125 lua_getfield(L, -1, "store");
126
127 if(lua_isnil(L, -1))
128 {
129 lua_pop(L, 3);
130 dt_lua_unlock();
131 g_free(filename);
132 return 0;
133 }
134
135 luaA_push_type(L, self->parameter_lua_type, self_data);
136 luaA_push(L, dt_lua_image_t, &imgid);
137 luaA_push_type(L, format->parameter_lua_type, fdata);
138 lua_pushstring(L, complete_name);
139 lua_pushinteger(L, num);
140 lua_pushinteger(L, total);
141 lua_pushboolean(L, high_quality);
142 push_lua_data(L, d);
143 dt_lua_goto_subtable(L, "extra");
144 dt_lua_treated_pcall(L, 8, 0);
145 lua_pop(L, 2);
146 dt_lua_unlock();
147 g_free(filename);
148 return false;
149 }
150
151 // FIXME: return 0 on success and 1 on error!
initialize_store_wrapper(struct dt_imageio_module_storage_t * self,dt_imageio_module_data_t * data,dt_imageio_module_format_t ** format,dt_imageio_module_data_t ** fdata,GList ** images,const gboolean high_quality,const gboolean upscale)152 static int initialize_store_wrapper(struct dt_imageio_module_storage_t *self, dt_imageio_module_data_t *data,
153 dt_imageio_module_format_t **format, dt_imageio_module_data_t **fdata,
154 GList **images, const gboolean high_quality, const gboolean upscale)
155 {
156 dt_lua_lock();
157 lua_State *L = darktable.lua_state.state;
158
159 lua_getfield(L, LUA_REGISTRYINDEX, "dt_lua_storages");
160 lua_getfield(L, -1, self->plugin_name);
161 lua_getfield(L, -1, "initialize_store");
162
163 if(lua_isnil(L, -1))
164 {
165 lua_pop(L, 3);
166 dt_lua_unlock();
167 return 1;
168 }
169
170 luaA_push_type(L, self->parameter_lua_type, data);
171 luaA_push_type(L, (*format)->parameter_lua_type, *fdata);
172
173 lua_newtable(L);
174 for(const GList *imgids = *images; imgids; imgids = g_list_next(imgids))
175 {
176 luaA_push(L, dt_lua_image_t, &(imgids->data));
177 luaL_ref(L, -2);
178 }
179 lua_pushboolean(L, high_quality);
180
181 lua_storage_t *d = (lua_storage_t *)data;
182 push_lua_data(L, d);
183 dt_lua_goto_subtable(L, "extra");
184
185 dt_lua_treated_pcall(L, 5, 1);
186 if(!lua_isnoneornil(L, -1))
187 {
188 g_list_free(*images);
189 if(lua_type(L, -1) != LUA_TTABLE)
190 {
191 dt_print(DT_DEBUG_LUA, "LUA ERROR initialization function of storage did not return nil or table\n");
192 dt_lua_unlock();
193 return 1;
194 }
195 GList *new_images = NULL;
196 lua_pushnil(L);
197 while(lua_next(L, -2))
198 {
199 dt_lua_image_t imgid;
200 luaA_to(L, dt_lua_image_t, &imgid, -1);
201 new_images = g_list_prepend(new_images, GINT_TO_POINTER(imgid));
202 lua_pop(L, 1);
203 }
204 new_images = g_list_reverse(new_images);
205 *images = new_images;
206 }
207 lua_pop(L, 3);
208 dt_lua_unlock();
209 return 0;
210 }
finalize_store_wrapper(struct dt_imageio_module_storage_t * self,dt_imageio_module_data_t * data)211 static void finalize_store_wrapper(struct dt_imageio_module_storage_t *self, dt_imageio_module_data_t *data)
212 {
213 dt_lua_lock();
214 lua_State *L = darktable.lua_state.state;
215
216 lua_getfield(L, LUA_REGISTRYINDEX, "dt_lua_storages");
217 lua_getfield(L, -1, self->plugin_name);
218 lua_getfield(L, -1, "finalize_store");
219
220 if(lua_isnil(L, -1))
221 {
222 lua_pop(L, 3);
223 dt_lua_unlock();
224 return;
225 }
226
227 luaA_push_type(L, self->parameter_lua_type, data);
228
229 lua_storage_t *d = (lua_storage_t *)data;
230 push_lua_data(L, d);
231 dt_lua_goto_subtable(L, "files");
232
233 push_lua_data(L, d);
234 dt_lua_goto_subtable(L, "extra");
235
236 dt_lua_treated_pcall(L, 3, 0);
237 lua_pop(L, 2);
238 dt_lua_unlock();
239 }
params_size_wrapper(struct dt_imageio_module_storage_t * self)240 static size_t params_size_wrapper(struct dt_imageio_module_storage_t *self)
241 {
242 return 0;
243 }
get_params_wrapper(struct dt_imageio_module_storage_t * self)244 static void *get_params_wrapper(struct dt_imageio_module_storage_t *self)
245 {
246 lua_storage_t *d = malloc(sizeof(lua_storage_t));
247 d->data_created = false;
248 return d;
249 }
250
251 typedef struct
252 {
253 lua_storage_t *data;
254 } free_param_wrapper_data;
255
free_param_wrapper_destroy(void * data)256 static void free_param_wrapper_destroy(void * data)
257 {
258 if(!data) return;
259 free_param_wrapper_data *params = data;
260 lua_storage_t *d = params->data;
261 if(d->data_created)
262 {
263 // if we reach here, then the main job hasn't been executed.
264 // This means that we are in an error path, and might be in the GUI thread
265 // we take the lock anyway to avoid a memory leak, but this might freeze the UI
266 dt_lua_lock();
267 lua_pushlightuserdata(darktable.lua_state.state, d);
268 lua_pushnil(darktable.lua_state.state);
269 lua_settable(darktable.lua_state.state, LUA_REGISTRYINDEX);
270 dt_lua_unlock();
271 }
272 free(d);
273 free(params);
274 }
free_param_wrapper_job(dt_job_t * job)275 static int32_t free_param_wrapper_job(dt_job_t *job)
276 {
277 free_param_wrapper_data *params = dt_control_job_get_params(job);
278 lua_storage_t *d = params->data;
279 if(d->data_created)
280 {
281 dt_lua_lock();
282 lua_pushlightuserdata(darktable.lua_state.state, d);
283 lua_pushnil(darktable.lua_state.state);
284 lua_settable(darktable.lua_state.state, LUA_REGISTRYINDEX);
285 dt_lua_unlock();
286 d->data_created = false;
287 }
288 return 0;
289 }
290
291
free_params_wrapper(struct dt_imageio_module_storage_t * self,dt_imageio_module_data_t * data)292 static void free_params_wrapper(struct dt_imageio_module_storage_t *self, dt_imageio_module_data_t *data)
293 {
294 dt_job_t *job = dt_control_job_create(&free_param_wrapper_job, "lua: destroy storage param");
295 if(!job) return;
296 free_param_wrapper_data *t = (free_param_wrapper_data *)calloc(1, sizeof(free_param_wrapper_data));
297 if(!t)
298 {
299 dt_control_job_dispose(job);
300 return;
301 }
302 dt_control_job_set_params(job, t, free_param_wrapper_destroy);
303 t->data = (lua_storage_t *)data;
304 dt_control_add_job(darktable.control, DT_JOB_QUEUE_SYSTEM_BG, job);
305 }
306
set_params_wrapper(struct dt_imageio_module_storage_t * self,const void * params,const int size)307 static int set_params_wrapper(struct dt_imageio_module_storage_t *self, const void *params, const int size)
308 {
309 return 0;
310 }
311
ask_user_confirmation_wrapper(struct dt_imageio_module_storage_t * self)312 static char *ask_user_confirmation_wrapper(struct dt_imageio_module_storage_t *self)
313 {
314 return NULL;
315 }
316
version_wrapper()317 static int version_wrapper()
318 {
319 return 0;
320 }
321
gui_init_wrapper(struct dt_imageio_module_storage_t * self)322 static void gui_init_wrapper(struct dt_imageio_module_storage_t *self)
323 {
324 lua_storage_gui_t *gui_data = self->gui_data;
325 self->widget = gui_data->widget->widget;
326 }
327
gui_reset_wrapper(struct dt_imageio_module_storage_t * self)328 static void gui_reset_wrapper(struct dt_imageio_module_storage_t *self)
329 {
330 lua_storage_gui_t *gui_data = self->gui_data;
331 dt_lua_async_call_alien(dt_lua_widget_trigger_callback,
332 0, NULL, NULL,
333 LUA_ASYNC_TYPENAME, "lua_widget", gui_data->widget,
334 LUA_ASYNC_TYPENAME, "const char*", "reset",
335 LUA_ASYNC_DONE);
336 }
337
gui_cleanup_wrapper(struct dt_imageio_module_storage_t * self)338 static void gui_cleanup_wrapper(struct dt_imageio_module_storage_t *self)
339 {
340 self->widget = NULL;
341 }
342
343
344 static dt_imageio_module_storage_t ref_storage = {
345 .plugin_name = { 0 },
346 .module = NULL,
347 .widget = NULL,
348 .gui_data = NULL,
349 .name = name_wrapper,
350 .gui_init = gui_init_wrapper,
351 .gui_cleanup = gui_cleanup_wrapper,
352 .gui_reset = gui_reset_wrapper,
353 .init = NULL,
354 .supported = default_supported_wrapper,
355 .dimension = default_dimension_wrapper,
356 .recommended_dimension = default_dimension_wrapper,
357 .store = store_wrapper,
358 .finalize_store = finalize_store_wrapper,
359 .initialize_store = initialize_store_wrapper,
360 .params_size = params_size_wrapper,
361 .get_params = get_params_wrapper,
362 .free_params = free_params_wrapper,
363 .set_params = set_params_wrapper,
364 .export_dispatched = empty_wrapper,
365 .ask_user_confirmation = ask_user_confirmation_wrapper,
366 .parameter_lua_type = LUAA_INVALID_TYPE,
367 .version = version_wrapper,
368
369 };
370
371
372
register_storage(lua_State * L)373 static int register_storage(lua_State *L)
374 {
375 lua_settop(L, 7);
376 lua_getfield(L, LUA_REGISTRYINDEX, "dt_lua_storages");
377 lua_newtable(L);
378
379 dt_imageio_module_storage_t *storage = malloc(sizeof(dt_imageio_module_storage_t));
380 memcpy(storage, &ref_storage, sizeof(dt_imageio_module_storage_t));
381 storage->gui_data = malloc(sizeof(lua_storage_gui_t));
382 lua_storage_gui_t *data = storage->gui_data;
383
384 const char *plugin_name = luaL_checkstring(L, 1);
385 lua_pushvalue(L, 1);
386 lua_setfield(L, -2, "plugin_name");
387 g_strlcpy(storage->plugin_name, plugin_name, sizeof(storage->plugin_name));
388
389 const char *name = luaL_checkstring(L, 2);
390 lua_pushvalue(L, 2);
391 lua_setfield(L, -2, "name");
392 data->name = strdup(name);
393 data->supported_formats = NULL;
394 data->widget = NULL;
395
396 if(!lua_isnoneornil(L, 3))
397 {
398 luaL_checktype(L, 3, LUA_TFUNCTION);
399 lua_pushvalue(L, 3);
400 lua_setfield(L, -2, "store");
401 }
402
403 if(lua_isnil(L, 4))
404 {
405 storage->finalize_store = NULL;
406 }
407 else
408 {
409 luaL_checktype(L, 4, LUA_TFUNCTION);
410 lua_pushvalue(L, 4);
411 lua_setfield(L, -2, "finalize_store");
412 }
413
414 if(!lua_isnoneornil(L, 5))
415 {
416 luaL_checktype(L, 5, LUA_TFUNCTION);
417 lua_pushvalue(L, 5);
418 lua_setfield(L, -2, "supported");
419 }
420
421 if(lua_isnil(L, 6))
422 {
423 storage->initialize_store = NULL;
424 }
425 else
426 {
427 luaL_checktype(L, 6, LUA_TFUNCTION);
428 lua_pushvalue(L, 6);
429 lua_setfield(L, -2, "initialize_store");
430 }
431
432 if(lua_isnil(L, 7))
433 {
434 storage->gui_init = empty_wrapper;
435 storage->gui_reset = empty_wrapper;
436 storage->gui_cleanup = empty_wrapper;
437 }
438 else
439 {
440 lua_widget widget;
441 luaA_to(L, lua_widget, &widget, 7);
442 dt_lua_widget_bind(L, widget);
443 data->widget = widget;
444 }
445
446
447 lua_setfield(L, -2, plugin_name);
448
449 char tmp[1024];
450 snprintf(tmp, sizeof(tmp), "dt_imageio_module_data_pseudo_%s", storage->plugin_name);
451 luaA_Type type_id = luaA_type_add(L, tmp, storage->params_size(storage));
452 storage->parameter_lua_type = dt_lua_init_type_type(darktable.lua_state.state, type_id);
453 luaA_struct_type(darktable.lua_state.state, type_id);
454 dt_lua_register_storage_type(darktable.lua_state.state, storage, type_id);
455
456 if(!lua_isnoneornil(L, 5))
457 {
458 for(GList *it = darktable.imageio->plugins_format; it; it = g_list_next(it))
459 {
460 lua_pushvalue(L, 5);
461 dt_imageio_module_format_t *format = (dt_imageio_module_format_t *)it->data;
462 dt_imageio_module_data_t *sdata = storage->get_params(storage);
463 dt_imageio_module_data_t *fdata = format->get_params(format);
464 luaA_push_type(L, storage->parameter_lua_type, sdata);
465 luaA_push_type(L, format->parameter_lua_type, fdata);
466 format->free_params(format, fdata);
467 storage->free_params(storage, sdata);
468 dt_lua_treated_pcall(L, 2, 1);
469 int result = lua_toboolean(L, -1);
470 lua_pop(L, 1);
471 if(result)
472 {
473 data->supported_formats = g_list_prepend(data->supported_formats, format);
474 }
475 }
476 }
477 else
478 {
479 // all formats are supported
480 for(GList *it = darktable.imageio->plugins_format; it; it = g_list_next(it))
481 {
482 dt_imageio_module_format_t *format = (dt_imageio_module_format_t *)it->data;
483 data->supported_formats = g_list_prepend(data->supported_formats, format);
484 }
485 }
486
487 storage->gui_init(storage);
488 if(storage->widget) g_object_ref(storage->widget);
489 dt_imageio_insert_storage(storage);
490
491 return 0;
492 }
493
destroy_storage(lua_State * L)494 static int destroy_storage(lua_State *L)
495 {
496 const char *module_name = luaL_checkstring(L, 1);
497 dt_imageio_module_storage_t *storage = dt_imageio_get_storage_by_name(module_name);
498 dt_imageio_remove_storage(storage);
499 // free the storage?
500 storage->gui_cleanup(storage);
501 if(storage->widget) g_object_unref(storage->widget);
502 if(storage->module) g_module_close(storage->module);
503 free(storage);
504 return 0;
505 }
506
dt_lua_init_luastorages(lua_State * L)507 int dt_lua_init_luastorages(lua_State *L)
508 {
509 dt_lua_push_darktable_lib(L);
510 lua_pushstring(L, "destroy_storage");
511 lua_pushcfunction(L, &destroy_storage);
512 lua_settable(L, -3);
513 lua_pop(L, 1);
514
515 dt_lua_push_darktable_lib(L);
516 lua_pushstring(L, "register_storage");
517 lua_pushcfunction(L, ®ister_storage);
518 lua_settable(L, -3);
519 lua_pop(L, 1);
520
521 lua_newtable(L);
522 lua_setfield(L, LUA_REGISTRYINDEX, "dt_lua_storages");
523 return 0;
524 }
525
526 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
527 // vim: shiftwidth=2 expandtab tabstop=2 cindent
528 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
529