![]() |
Ansel 0.0
A darktable fork - bloat + design vision
|
Merge histories, including optional pipeline topology merge. More...
#include "common/history_merge.h"#include "common/history_merge_gui.h"#include "common/darktable.h"#include "common/debug.h"#include "common/iop_order.h"#include "common/topological_sort.h"#include "develop/blend.h"#include "develop/dev_history.h"#include "develop/develop.h"#include "develop/imageop.h"#include <glib.h>#include <stdlib.h>#include <string.h>
Include dependency graph for history_merge.c:Data Structures | |
| struct | _hm_id_info_t |
| struct | _hm_dest_backup_t |
| struct | _hm_topo_merge_ctx_t |
Typedefs | |
| typedef struct _hm_topo_merge_ctx_t | _hm_topo_merge_ctx_t |
Enumerations | |
| enum | _hm_id_origin_t { HM_ID_FROM_MOD_LIST = 1 << 0 , HM_ID_FROM_SRC_IOP = 1 << 1 , HM_ID_FROM_DST_IOP = 1 << 2 , HM_ID_FROM_RULE = 1 << 3 } |
Functions | |
| char * | _hm_make_node_id (const char *op, const char *multi_name) |
| static void | _hm_free_input_node (dt_digraph_node_t *n) |
| static void | _hm_free_input_nodes (GList *input_nodes) |
| void | _hm_id_to_op_name (const char *id, char *op, char *name) |
| static int | _hm_build_prev_map_from_ids (const GList *ids, GHashTable **out_prev) |
| static int | _hm_build_next_map_from_ids (const GList *ids, GHashTable **out_next) |
| static gboolean | _hm_node_has_predecessor (const dt_digraph_node_t *n, const dt_digraph_node_t *pred) |
| static void | _hm_remove_predecessor (dt_digraph_node_t *n, const dt_digraph_node_t *pred) |
| static int | _hm_build_last_history_by_id_from_history (GList *history, const int history_end, GHashTable **out_map) |
| static int | _hm_backup_dest (const dt_develop_t *dev_dest, const GHashTable *mod_list_ids, _hm_dest_backup_t *backup) |
| static void | _hm_restore_dest_from_backup (dt_develop_t *dev_dest, _hm_dest_backup_t *backup) |
| static void | _hm_backup_cleanup (_hm_dest_backup_t *backup) |
| int | _hm_build_last_history_by_id (const dt_develop_t *dev, GHashTable **out_map) |
| static int | _hm_build_id_set_from_mod_list (const GList *mod_list, GHashTable **out_ids) |
| static _hm_id_info_t * | _hm_id_info_upsert (GHashTable *id_ht, const char *op, const char *multi_name, const _hm_id_origin_t origin, const dt_iop_module_t *mod_list, const dt_iop_module_t *src_iop, dt_iop_module_t *dst_iop) |
| static int | _hm_ids_from_iop_list (GList *iop, GHashTable *id_ht, const guint keep_mask, GList **out_ids) |
| static int | _hm_build_input_nodes_from_ids (const GList *ids, const char *tag, GList **out_nodes) |
| static int | _hm_build_input_nodes_from_ids_filtered (const GList *ids, const char *tag, const GHashTable *focus, GList **out_nodes) |
| static int | _hm_build_isolated_nodes_from_modules (const GList *modules, const char *tag, GList **out_nodes) |
| static int | _hm_build_raster_mask_nodes_from_modules (const GList *modules, GHashTable *id_ht, const guint keep_mask, const char *tag, GList **out_nodes) |
| static int | _iop_rules (GHashTable *keep, GList **out_nodes) |
| static void | _hm_topo_merge_cleanup (_hm_topo_merge_ctx_t *ctx) |
| static int | _hm_topo_build_id_info_table (_hm_topo_merge_ctx_t *ctx, dt_develop_t *dev_dest, dt_develop_t *dev_src, const GList *mod_list) |
| static int | _hm_topo_build_constraint_ids (_hm_topo_merge_ctx_t *ctx, dt_develop_t *dev_dest, dt_develop_t *dev_src, const GList *mod_list, const gboolean merge_iop_order) |
| static int | _hm_topo_resolve_incompatible_constraints (GList *flat, GHashTable *id_ht, const GList *src_ids, const GList *dest_ids) |
| static int | _hm_topo_flatten_constraints (_hm_topo_merge_ctx_t *ctx) |
| static int | _hm_topo_sort_constraints (_hm_topo_merge_ctx_t *ctx) |
| static int | _hm_topo_apply_solution (_hm_topo_merge_ctx_t *ctx, dt_develop_t *dev_dest, dt_develop_t *dev_src) |
| static int | _hm_try_merge_iop_order_topologically (dt_develop_t *dev_dest, dt_develop_t *dev_src, const GList *mod_list, const gboolean merge_iop_order) |
| static void | _hm_renumber_history (GList *history) |
| static void | _hm_truncate_dest_redo_tail (dt_develop_t *dev_dest) |
| int | dt_history_merge (dt_develop_t *dev_dest, dt_develop_t *dev_src, const int32_t dest_imgid, const GList *mod_list, const gboolean merge_iop_order, const dt_history_merge_strategy_t strategy, const gboolean force_new_modules) |
| Merge a list of modules into a destination image, solving pipeline topologies for proper insertion of source modules. | |
Merge histories, including optional pipeline topology merge.
Merge histories, for copy-pasting and styles.
Merging histories is a twofold problems, we need to solve :
Let 2 histories A and B, considering A is our existing one, and B is our candidate for pasting. They each have a devA and devB dt_develop_t object. The members of interest are dev->history, dev->iop, dev->iop_order
The first problem is simple : we have append mode (concatenate devB->history at the end of devA->history) or appstart mode (concatenate devB->history at the start of devA->history), decided by user. Histories are commited to pipeline nodes ("popped") in the order of items. Since each history item may overwrite any previous item targetting the same module, this order defines which history takes precedence over the other, in case of conflicts. We do not handle per-module conflict resolution.
The second problem is much harder, because both histories may have different topologies (number of nodes and relative ordering). For this, we need to build temporary pipelines devA->iop and devB->iop using each histories, and turn them into directed graphs. Each module will be a digraph node defined by its previous and next module in its original pipeline, identified by its module->op and module->multi_name, taken as unique ID. Modules having the same ID in both pipes will be assumed to be the same entity and merged. Then we will need a topological sort to resolve (if possible) the complete pipeline order, taking into account the ordering constraints imposed by devA and by devB, which may overconstrain each entity. Once we have the topological order sorted, we will need to create a new pipeline and apply ("pop") the concatenated histories as solved in the first problem.
This supposes users provided an explicit pipeline order as a { module -> index } list.
However, when users don't specify a pipeline order in the merge, we are to assume they don't care. In this case, we enforce a behaviour that is consistent with GUI operation :
module->op and module->multi_name between A and B, are also directly matched (no re-ordering, no new instance),There is also a special case for a "force new modules" merge. In this mode, all modules from B will be added to A as new instances. This will need to rename and reindex instances properly for the B modules, then update B history accordingly. Pipeline topology will obey the above rules, depending whether an explicit pipeline order was specified or not.
If A does not exist yet, it has to be inited with all the typical defaults applied to new edits. This is done automatically in dt_dev_history_read_history(), which either load history from database or init a fresh one, along with a pipeline (dev->iop).
Before popping the history into modules (as pipeline nodes), each history item will need to be resynced with nodes, especially the pipe order and dt_iop_module_t * references. Here again, module->op and module->multi_name act as unique IDs to match history entries and pipe nodes. History entries that don't find an existing module will need a new module to be created.
| typedef struct _hm_topo_merge_ctx_t _hm_topo_merge_ctx_t |
| enum _hm_id_origin_t |
|
static |
|
static |
References _hm_build_last_history_by_id_from_history(), _hm_collect_labels_from_history_map(), dt_dev_get_history_end_ext(), dt_history_duplicate(), dt_ioppr_iop_order_copy_deep(), _hm_dest_backup_t::history, dt_develop_t::history, _hm_dest_backup_t::history_end, _hm_dest_backup_t::iop_order_list, dt_develop_t::iop_order_list, _hm_dest_backup_t::orig_ids, _hm_dest_backup_t::orig_labels, _hm_dest_backup_t::orig_styles, and TRUE.
Referenced by dt_history_merge().
|
static |
References _hm_make_node_id(), dt_free_gpointer(), dt_iop_module_t::multi_name, and dt_iop_module_t::op.
Referenced by dt_history_merge().
|
static |
References _hm_free_input_node(), _hm_free_input_nodes(), dt_digraph_node_new(), and n.
Referenced by _hm_topo_flatten_constraints().
|
static |
References _hm_free_input_node(), _hm_free_input_nodes(), dt_digraph_node_new(), dt_digraph_node_t::id, and n.
Referenced by _hm_topo_flatten_constraints().
|
static |
References _hm_free_input_node(), _hm_free_input_nodes(), _hm_make_node_id(), dt_digraph_node_new(), dt_free, dt_iop_module_t::multi_name, n, and dt_iop_module_t::op.
Referenced by _hm_topo_flatten_constraints().
| int _hm_build_last_history_by_id | ( | const dt_develop_t * | dev, |
| GHashTable ** | out_map | ||
| ) |
|
static |
References _hm_make_node_id(), dt_free_gpointer(), dt_dev_history_item_t::multi_name, and dt_dev_history_item_t::op_name.
Referenced by _hm_backup_dest().
|
static |
References dt_free_gpointer().
Referenced by _hm_topo_resolve_incompatible_constraints().
|
static |
References dt_free_gpointer().
Referenced by _hm_topo_resolve_incompatible_constraints().
|
static |
References _hm_free_input_node(), _hm_free_input_nodes(), _hm_make_node_id(), dt_digraph_node_new(), dt_free, _hm_id_info_t::flags, dt_iop_module_t::multi_name, dt_iop_module_t::op, dt_digraph_node_t::previous, dt_iop_module_t::raster_mask, dt_iop_module_t::sink, dt_iop_module_t::source, and dt_digraph_node_t::tag.
Referenced by _hm_topo_flatten_constraints().
|
static |
|
static |
|
static |
References _hm_make_node_id(), _hm_id_info_t::dst_iop, DT_DEBUG_HISTORY, dt_free, dt_print(), _hm_id_info_t::flags, HM_ID_FROM_MOD_LIST, _hm_id_info_t::mod_list, and _hm_id_info_t::src_iop.
Referenced by _hm_topo_build_id_info_table().
| void _hm_id_to_op_name | ( | const char * | id, |
| char * | op, | ||
| char * | name | ||
| ) |
Referenced by _hm_module_from_id(), _hm_pretty_id(), and _hm_topo_apply_solution().
|
static |
References _hm_make_node_id(), dt_free, _hm_id_info_t::flags, dt_iop_module_t::multi_name, and dt_iop_module_t::op.
Referenced by _hm_topo_build_constraint_ids().
| char * _hm_make_node_id | ( | const char * | op, |
| const char * | multi_name | ||
| ) |
Referenced by _hm_build_id_set_from_mod_list(), _hm_build_isolated_nodes_from_modules(), _hm_build_last_history_by_id(), _hm_build_last_history_by_id_from_history(), _hm_build_override_map(), _hm_build_raster_mask_nodes_from_modules(), _hm_id_info_upsert(), _hm_ids_from_iop_list(), _hm_module_visible_in_report(), _hm_report_build_moved_set(), _hm_report_dest_label(), _hm_show_merge_report_popup(), and _hm_topo_build_constraint_ids().
|
static |
References FALSE, n, p, and TRUE.
Referenced by _hm_topo_resolve_incompatible_constraints().
|
static |
References n.
Referenced by _hm_topo_resolve_incompatible_constraints().
|
static |
References h.
Referenced by dt_history_merge().
|
static |
References dt_dev_history_free_history(), dt_dev_pop_history_items_ext(), dt_dev_set_history_end_ext(), dt_dev_write_history_ext(), dt_free_gpointer(), _hm_dest_backup_t::history, dt_develop_t::history, _hm_dest_backup_t::history_end, dt_image_t::id, dt_develop_t::image_storage, _hm_dest_backup_t::iop_order_list, and dt_develop_t::iop_order_list.
Referenced by dt_history_merge().
|
static |
References _hm_id_to_op_name(), _hm_topo_merge_ctx_t::copy_module_contents, _hm_id_info_t::dst_iop, DT_DEBUG_HISTORY, dt_dev_copy_module_contents(), dt_dev_create_module_instance(), dt_dev_get_module_instance(), dt_ioppr_rebuild_iop_order_from_modules(), dt_print(), _hm_id_info_t::flags, HM_ID_FROM_DST_IOP, _hm_topo_merge_ctx_t::id_ht, _hm_topo_merge_ctx_t::keep_mask, _hm_id_info_t::mod_list, n, name, _hm_topo_merge_ctx_t::sorted, and TRUE.
Referenced by _hm_try_merge_iop_order_topologically().
|
static |
References _hm_ids_from_iop_list(), _hm_make_node_id(), _hm_topo_merge_ctx_t::dest_ids, DT_DEBUG_HISTORY, dt_free, dt_free_gpointer(), dt_print(), _hm_id_info_t::flags, HM_ID_FROM_DST_IOP, HM_ID_FROM_MOD_LIST, HM_ID_FROM_RULE, _hm_topo_merge_ctx_t::id_ht, dt_develop_t::iop, _hm_topo_merge_ctx_t::keep_mask, dt_iop_module_t::multi_name, dt_iop_module_t::op, _hm_topo_merge_ctx_t::src_focus_ids, and _hm_topo_merge_ctx_t::src_ids.
Referenced by _hm_try_merge_iop_order_topologically().
|
static |
References _hm_id_info_upsert(), darktable, dt_free_gpointer(), HM_ID_FROM_DST_IOP, HM_ID_FROM_MOD_LIST, HM_ID_FROM_RULE, HM_ID_FROM_SRC_IOP, _hm_topo_merge_ctx_t::id_ht, dt_develop_t::iop, darktable_t::iop_order_rules, dt_iop_module_t::multi_name, dt_iop_module_t::op, dt_iop_order_rule_t::op_next, and dt_iop_order_rule_t::op_prev.
Referenced by _hm_try_merge_iop_order_topologically().
|
static |
References _hm_build_input_nodes_from_ids(), _hm_build_input_nodes_from_ids_filtered(), _hm_build_isolated_nodes_from_modules(), _hm_build_raster_mask_nodes_from_modules(), _hm_free_input_nodes(), _hm_topo_resolve_incompatible_constraints(), _iop_rules(), _hm_topo_merge_ctx_t::dest_ids, _hm_topo_merge_ctx_t::dev_dest, DT_DEBUG_HISTORY, dt_print(), error(), _hm_topo_merge_ctx_t::flat, flatten_nodes(), _hm_topo_merge_ctx_t::id_ht, _hm_topo_merge_ctx_t::input_nodes, dt_develop_t::iop, _hm_topo_merge_ctx_t::keep_mask, _hm_topo_merge_ctx_t::mod_list, _hm_topo_merge_ctx_t::src_focus_ids, and _hm_topo_merge_ctx_t::src_ids.
Referenced by _hm_try_merge_iop_order_topologically().
|
static |
References _hm_free_input_nodes(), _hm_topo_merge_ctx_t::dest_ids, dt_digraph_cleanup_full(), dt_free_gpointer(), _hm_topo_merge_ctx_t::flat, _hm_topo_merge_ctx_t::id_ht, _hm_topo_merge_ctx_t::input_nodes, _hm_topo_merge_ctx_t::sorted, _hm_topo_merge_ctx_t::src_focus_ids, and _hm_topo_merge_ctx_t::src_ids.
Referenced by _hm_try_merge_iop_order_topologically().
|
static |
References _hm_ask_user_constraints_choice(), _hm_build_next_map_from_ids(), _hm_build_prev_map_from_ids(), _hm_node_has_predecessor(), _hm_remove_predecessor(), a, b, c, cleanup(), DT_DEBUG_HISTORY, dt_free, dt_free_gpointer(), DT_HM_CONSTRAINTS_PREFER_SRC, dt_print(), _hm_id_info_t::flags, HM_ID_FROM_MOD_LIST, dt_digraph_node_t::id, key, and p.
Referenced by _hm_topo_flatten_constraints().
|
static |
|
static |
References DT_DEBUG_HISTORY, dt_dev_free_history_item(), dt_dev_get_history_end_ext(), dt_print(), and dt_develop_t::history.
Referenced by dt_history_merge().
|
static |
References _hm_topo_apply_solution(), _hm_topo_build_constraint_ids(), _hm_topo_build_id_info_table(), _hm_topo_flatten_constraints(), _hm_topo_merge_cleanup(), _hm_topo_sort_constraints(), _hm_topo_merge_ctx_t::copy_module_contents, _hm_topo_merge_ctx_t::dev_dest, and _hm_topo_merge_ctx_t::mod_list.
Referenced by dt_history_merge().
|
static |
| int dt_history_merge | ( | struct dt_develop_t * | dev_dest, |
| struct dt_develop_t * | dev_src, | ||
| const int32_t | dest_imgid, | ||
| const GList * | mod_list, | ||
| const gboolean | merge_iop_order, | ||
| const dt_history_merge_strategy_t | strategy, | ||
| const gboolean | force_new_modules | ||
| ) |
Merge a list of modules into a destination image, solving pipeline topologies for proper insertion of source modules.
| dev_dest | Destination develop stack (must be initialized, history read and popped). |
| dev_src | Source develop stack (must be initialized, history read and popped). May be NULL if merge_iop_order is FALSE (masks won't be copied). |
| dest_imgid | Destination image id. |
| mod_list | List of dt_iop_module_t* to merge (usually coming from dev_src). |
| merge_iop_order | If TRUE, attempt to merge the pipeline order constraints from src and dest using a topological sort. On unsatisfiable constraints, falls back to overwriting the destination iop-order list with the source list. |
| strategy | DT_HISTORY_MERGE_APPEND or DT_HISTORY_MERGE_APPSTART. |
| force_new_modules | If TRUE, always add modules from source as new instances (when possible). |
References _hm_backup_cleanup(), _hm_backup_dest(), _hm_build_id_set_from_mod_list(), _hm_build_last_history_by_id(), _hm_renumber_history(), _hm_restore_dest_from_backup(), _hm_show_merge_report_popup(), _hm_truncate_dest_redo_tail(), _hm_try_merge_iop_order_topologically(), _hm_warn_missing_raster_producers(), cleanup(), DT_DEBUG_HISTORY, dt_dev_get_history_end_ext(), dt_dev_get_module_instance(), dt_dev_history_get_last_item_by_module(), dt_dev_history_item_from_source_history_item(), dt_dev_set_history_end_ext(), DT_HISTORY_MERGE_APPEND, dt_ioppr_resync_pipeline(), dt_print(), FALSE, dt_develop_t::history, dt_iop_module_t::multi_name, dt_iop_module_t::multi_priority, dt_iop_module_t::op, _hm_dest_backup_t::orig_ids, _hm_dest_backup_t::orig_labels, and _hm_dest_backup_t::orig_styles.
Referenced by dt_dev_merge_history_into_image().