![]() |
Ansel 0.0
A darktable fork - bloat + design vision
|
#include "develop/geometry/geometry.h"#include "common/logging.h"#include "develop/dev_geometry.h"#include "develop/dev_history.h"#include "develop/develop.h"#include "develop/imageop.h"#include "system/mem_alloc.h"#include <math.h>#include <string.h>
Include dependency graph for geometry.c:Go to the source code of this file.
Data Structures | |
| struct | dt_geometry_chain_t |
Functions | |
| static gboolean | _on_roster (const char *op) |
| static void | _record_free (void *ptr) |
| static void | _chain_clear (dt_geometry_chain_t *chain) |
| dt_geometry_chain_t * | dt_geometry_chain_new (void) |
| void | dt_geometry_chain_free (dt_geometry_chain_t *chain) |
| static gint | _by_iop_order (gconstpointer a, gconstpointer b) |
| static gboolean | _suppressed_by_focus (const dt_geometry_chain_t *chain, const dt_geometry_record_t *record) |
| Is this record disabled for the current query by the focused module? | |
| static void | _fold_sizes (dt_geometry_chain_t *chain) |
| Fold every record's map_size in pipe order, at full resolution and scale 1. | |
| static gboolean | _resolve_from_history (dt_develop_t *dev, dt_iop_module_t *module, const int32_t history_end, const void **params) |
| What will this module be committed with? Mirrors the pipe's own resolution. | |
| void | dt_geometry_chain_rebuild (dt_develop_t *dev) |
| Rebuild the chain from the dev's current modules and history. GUI thread only. | |
| uint64_t | dt_geometry_chain_generation (const dt_geometry_chain_t *chain) |
| How many times this chain has been rebuilt. A GUI cache key for anything derived from the composed geometry. | |
| gboolean | dt_geometry_chain_authoritative (const dt_geometry_chain_t *chain) |
| Can this chain answer questions yet? | |
| gboolean | dt_geometry_chain_processed_size (const dt_geometry_chain_t *chain, int *width, int *height) |
| The developed image's full-resolution size, from the chain's own fold. | |
| const dt_geometry_record_t * | dt_geometry_chain_find (const dt_geometry_chain_t *chain, const char *op, const int instance) |
| One module instance's record, or NULL. Use it for that module's own in/out dims. | |
| static gboolean | _in_bound (const dt_geometry_record_t *record, const double iop_order, const int direction) |
| Does this record fall inside the requested bound? | |
| static int | _compose_forward (dt_geometry_chain_t *chain, const double iop_order, const int direction, float *points, const size_t points_count) |
| The forward fold over a chain, shared by the public entry point and by dt_geometry_chain_compose(). Assumes the chain is usable; the callers check. | |
| int | dt_geometry_module_transform (dt_develop_t *dev, const dt_iop_module_t *module, float *points, const size_t points_count) |
| int | dt_geometry_chain_compose (dt_geometry_chain_t *chain, const double iop_order, const int direction, float *points, const size_t points_count) |
Compose the chain over points, for a record evaluator that needs the transform stack around its own module. | |
| int | dt_geometry_transform (dt_develop_t *dev, const double iop_order, const int direction, float *points, const size_t points_count) |
Compose forward over the chain, in place. direction is a DT_DEV_TRANSFORM_DIR_*. | |
| int | dt_geometry_backtransform (dt_develop_t *dev, const double iop_order, const int direction, float *points, const size_t points_count) |
| Compose backward over the chain, in place. | |
| void | dt_geometry_self_check (dt_develop_t *dev, const double chain_ms) |
| What the shadow harness became once there was nothing left to shadow. | |
Variables | |
| static const char *const | _roster [] |
|
static |
Definition at line 131 of file geometry.c.
References dt_geometry_record_t::iop_order.
Referenced by dt_geometry_chain_rebuild().
|
static |
Definition at line 106 of file geometry.c.
References _record_free(), dt_geometry_chain_t::authoritative, dt_free_gpointer(), FALSE, dt_geometry_chain_t::missing, dt_geometry_chain_t::processed_height, dt_geometry_chain_t::processed_width, dt_geometry_chain_t::records, and dt_geometry_chain_t::sized.
Referenced by dt_geometry_chain_free(), and dt_geometry_chain_rebuild().
|
static |
The forward fold over a chain, shared by the public entry point and by dt_geometry_chain_compose(). Assumes the chain is usable; the callers check.
Definition at line 372 of file geometry.c.
References _in_bound(), _suppressed_by_focus(), dt_geometry_record_t::data, IS_NULL_PTR, dt_geometry_chain_t::records, dt_geometry_vtable_t::transform, and dt_geometry_record_t::vtable.
Referenced by dt_geometry_chain_compose(), and dt_geometry_transform().
|
static |
Fold every record's map_size in pipe order, at full resolution and scale 1.
Reproduces dt_dev_pixelpipe_get_roi_out(): seed the input rect from the raw dimensions at scale 1, hand each enabled module its input and take its output as the next module's input, and record both on the way through. Those per-record rects are a product in their own right – consumers read their own module's input/output dimensions off them, and not only the geometric modules do (graduatednd has no geometry callbacks and reads its output rect for its overlay).
A record with no vtable, or one suppressed by the focused module, is identity. The suppression matters for SIZE and not only for coordinates: the fold this mirrors clears piece->enabled for such modules, so the developed size genuinely changes when the user enters crop's edit mode.
Definition at line 179 of file geometry.c.
References _suppressed_by_focus(), dt_geometry_record_t::data, dt_geometry_record_t::in, IS_NULL_PTR, dt_geometry_vtable_t::map_size, dt_geometry_record_t::out, dt_geometry_chain_t::processed_height, dt_geometry_chain_t::processed_width, dt_geometry_chain_t::raw_height, dt_geometry_chain_t::raw_width, dt_geometry_chain_t::records, dt_geometry_chain_t::sized, dt_geometry_record_t::vtable, and rect::width.
Referenced by dt_geometry_chain_rebuild().
|
static |
Does this record fall inside the requested bound?
The five modes are dt_dev_distort_transform_plus()'s, reproduced exactly. Every existing caller's bound has to keep meaning what it meant – the mask GUI in particular composes BACK_EXCL up to a module, shifts, then FORW_INCL back out, and a bound that shifted by one module would move every clone source by that module's transform.
Definition at line 357 of file geometry.c.
References DT_DEV_TRANSFORM_DIR_ALL, DT_DEV_TRANSFORM_DIR_BACK_EXCL, DT_DEV_TRANSFORM_DIR_BACK_INCL, DT_DEV_TRANSFORM_DIR_FORW_EXCL, DT_DEV_TRANSFORM_DIR_FORW_INCL, dt_geometry_record_t::iop_order, and TRUE.
Referenced by _compose_forward(), and dt_geometry_backtransform().
|
static |
Definition at line 90 of file geometry.c.
References _roster, FALSE, i, IS_NULL_PTR, and TRUE.
Referenced by dt_geometry_chain_rebuild().
Definition at line 98 of file geometry.c.
References dt_geometry_record_t::data, dt_free, dt_geometry_record_t::free_data, and IS_NULL_PTR.
Referenced by _chain_clear().
|
static |
What will this module be committed with? Mirrors the pipe's own resolution.
dt_dev_pixelpipe_change() decides a piece's parameters and its enabled state in two ways, and the chain has to reproduce both or it describes a geometry the pipes are not rendering:
What it must NOT read is module->params and module->enabled. Those are the GUI thread's live values, and dt_dev_add_history_item() is throttled, so between an edit and its commit they are ahead of what any pipe has been told – which shadow mode caught as a size divergence while the crop module's piece was mid-transition.
| params | out: the blob to hand geometry_record(). Borrowed, valid while history_mutex is held. |
Definition at line 223 of file geometry.c.
References dt_dev_history_item_t::enabled, enabled, dt_develop_t::history, IOP_FLAGS_NO_HISTORY_STACK, IS_NULL_PTR, and dt_dev_history_item_t::params.
Referenced by dt_geometry_chain_rebuild().
|
static |
Is this record disabled for the current query by the focused module?
The chain's copy of dt_dev_pixelpipe_activemodule_disables_currentmodule(). It is evaluated HERE, per query, and never stored in a record: the inputs are the focused module's identity, its tag filter, and whether it is in editing mode – all of which change with no history commit, which is precisely why records cannot carry them.
Definition at line 148 of file geometry.c.
References dt_geometry_chain_t::dev, dt_iop_get_cache_bypass(), FALSE, dt_develop_t::gui_attached, dt_develop_t::gui_module, dt_geometry_record_t::instance, IS_NULL_PTR, dt_iop_module_t::multi_priority, dt_geometry_record_t::op, dt_iop_module_t::op, and dt_geometry_record_t::operation_tags.
Referenced by _compose_forward(), _fold_sizes(), dt_geometry_backtransform(), and dt_geometry_module_transform().
| int dt_geometry_backtransform | ( | dt_develop_t * | dev, |
| const double | iop_order, | ||
| const int | direction, | ||
| float * | points, | ||
| const size_t | points_count | ||
| ) |
Compose backward over the chain, in place.
Definition at line 429 of file geometry.c.
References _in_bound(), _suppressed_by_focus(), dt_geometry_chain_t::authoritative, dt_geometry_vtable_t::backtransform, dt_geometry_record_t::data, dt_develop_t::geometry_chain, IS_NULL_PTR, dt_geometry_chain_t::records, and dt_geometry_record_t::vtable.
Referenced by dt_dev_coordinates_image_abs_to_raw_abs(), dt_dev_distort_backtransform_gui(), and dt_geometry_self_check().
| gboolean dt_geometry_chain_authoritative | ( | const dt_geometry_chain_t * | chain | ) |
Can this chain answer questions yet?
TRUE only when every enabled module the roster names has published a record. Authority is WHOLESALE: composing some modules from records and the rest from pipeline pieces would interleave two states, and the result would be wrong in a way that looks plausible.
There is no longer anything to fall back TO – the pipe this replaced is deleted – so a FALSE here is not a degraded mode, it is the GUI declining to answer: sizes come back FALSE, the transforms return 0 and leave their points untouched. Before an image is loaded that is simply the truth. After one is, it is a defect, and dt_geometry_self_check names which module owes a record.
Definition at line 324 of file geometry.c.
References dt_geometry_chain_t::authoritative, and IS_NULL_PTR.
Referenced by dt_dev_module_geometry_gui(), dt_dev_processed_size_gui(), gui_post_expose(), and main().
| int dt_geometry_chain_compose | ( | dt_geometry_chain_t * | chain, |
| double | iop_order, | ||
| int | direction, | ||
| float * | points, | ||
| size_t | points_count | ||
| ) |
Compose the chain over points, for a record evaluator that needs the transform stack around its own module.
The nested case, and the reason dt_geometry_vtable_t hands every evaluator the chain. iop/liquify.c is the one that needs it: its warps are stored in RAW sensor coordinates, so before it can rasterise anything it has to push its own path nodes through everything upstream of itself. On the pixel pipe it does that by re-entering the pipe walker mid-walk; here it re-enters this.
Bounded exactly like the walkers, so BACK_EXCL of the caller's own iop_order excludes the caller and the recursion terminates. Do not call it with a bound that includes the caller.
Definition at line 412 of file geometry.c.
References _compose_forward(), and IS_NULL_PTR.
Referenced by _distort_paths().
| const dt_geometry_record_t * dt_geometry_chain_find | ( | const dt_geometry_chain_t * | chain, |
| const char * | op, | ||
| const int | instance | ||
| ) |
One module instance's record, or NULL. Use it for that module's own in/out dims.
Definition at line 337 of file geometry.c.
References dt_geometry_record_t::data, dt_geometry_record_t::instance, IS_NULL_PTR, dt_geometry_record_t::op, and dt_geometry_chain_t::records.
Referenced by _flip_swaps_axes(), and dt_dev_module_geometry_gui().
| void dt_geometry_chain_free | ( | dt_geometry_chain_t * | chain | ) |
Definition at line 124 of file geometry.c.
References _chain_clear(), dt_free, and IS_NULL_PTR.
Referenced by dt_dev_cleanup().
| uint64_t dt_geometry_chain_generation | ( | const dt_geometry_chain_t * | chain | ) |
How many times this chain has been rebuilt. A GUI cache key for anything derived from the composed geometry.
A consumer that caches something it composed through this service – a mask outline in image coordinates, say – needs to know when to throw that cache away. The answer is "when the geometry moved", and this counter is that, cheaply: it advances once per rebuild, and a rebuild happens exactly where a pipe flag is raised, i.e. where the module stack or the history changed.
It is deliberately NOT a content hash. A rebuild that lands on identical geometry still advances it, which costs a consumer one redundant recompute; the alternative – hashing each record's module-owned data blob, whose size this service does not know – would have to guess, and a missed change here is an overlay drawn in the wrong place. Over-invalidating is the safe direction for a key.
What it must NOT be replaced by is a PIXEL identity. Keying an outline cache on a pipe's backbuffer hash, which is what iop/masks did before, ties a geometric fact to a rendering event: every republished preview frame – continuous while a brush is being dragged – then invalidates outlines whose inputs did not change. Measured on the report in #1158: 566 rebuilds of two brush outlines in 80 seconds, ~2 s of coordinate transform, gravity centres recomputed bit-identical every time, and a darkroom expose growing from 23 ms to 137 ms as strokes accumulated.
Definition at line 319 of file geometry.c.
References dt_geometry_chain_t::generation, and IS_NULL_PTR.
Referenced by _masks_draw_creation_session_forms(), dt_masks_gui_form_create(), dt_masks_gui_form_create_throttled(), and dt_masks_gui_form_test_create().
| dt_geometry_chain_t * dt_geometry_chain_new | ( | void | ) |
Definition at line 119 of file geometry.c.
Referenced by dt_dev_init(), and main().
| gboolean dt_geometry_chain_processed_size | ( | const dt_geometry_chain_t * | chain, |
| int * | width, | ||
| int * | height | ||
| ) |
The developed image's full-resolution size, from the chain's own fold.
Definition at line 329 of file geometry.c.
References FALSE, height, IS_NULL_PTR, dt_geometry_chain_t::processed_height, dt_geometry_chain_t::processed_width, dt_geometry_chain_t::sized, TRUE, and width.
Referenced by dt_dev_get_thumbnail_size(), and dt_dev_processed_size_gui().
| void dt_geometry_chain_rebuild | ( | struct dt_develop_t * | dev | ) |
Rebuild the chain from the dev's current modules and history. GUI thread only.
Called wherever the pixel-less pipe used to be resynchronised. Cheap by construction – a record is a small derivation of already-committed params, with no LUT, no colour transform and no disk access – which is the point: it runs in the same step as the history write, where its predecessor's 0.1-0.3 s could not.
Definition at line 247 of file geometry.c.
References _by_iop_order(), _chain_clear(), _fold_sizes(), _on_roster(), _resolve_from_history(), dt_geometry_chain_t::authoritative, dt_geometry_chain_t::dev, dt_dev_geometry_get_raw_size(), dt_dev_get_history_end_ext(), dt_pthread_rwlock_rdlock(), dt_pthread_rwlock_unlock(), dt_geometry_record_t::enabled, FALSE, dt_geometry_chain_t::generation, dt_develop_t::geometry_chain, dt_develop_t::history_mutex, dt_geometry_record_t::instance, dt_develop_t::iop, dt_geometry_record_t::iop_order, IS_NULL_PTR, dt_geometry_chain_t::missing, dt_geometry_record_t::op, dt_geometry_record_t::operation_tags, dt_geometry_chain_t::raw_height, dt_geometry_chain_t::raw_width, dt_geometry_chain_t::records, dt_geometry_chain_t::sized, and TRUE.
Referenced by _enter_edit_mode(), _set_frame(), do_crop(), dt_dev_get_thumbnail_size(), dt_dev_pixelpipe_rebuild_all_real(), dt_dev_pixelpipe_resync_history_preview_real(), dt_dev_pixelpipe_update_history_preview_real(), dt_dev_pixelpipe_update_zoom_main_real(), dt_dev_pixelpipe_update_zoom_preview_real(), and main().
| int dt_geometry_module_transform | ( | dt_develop_t * | dev, |
| const dt_iop_module_t * | module, | ||
| float * | points, | ||
| const size_t | points_count | ||
| ) |
Definition at line 386 of file geometry.c.
References _suppressed_by_focus(), dt_geometry_chain_t::authoritative, dt_geometry_record_t::data, dt_geometry_record_t::enabled, dt_develop_t::geometry_chain, dt_geometry_record_t::instance, IS_NULL_PTR, dt_iop_module_t::multi_priority, dt_geometry_record_t::op, dt_iop_module_t::op, dt_geometry_chain_t::records, dt_geometry_vtable_t::transform, and dt_geometry_record_t::vtable.
Referenced by call_distort_transform().
| void dt_geometry_self_check | ( | dt_develop_t * | dev, |
| const double | chain_ms | ||
| ) |
What the shadow harness became once there was nothing left to shadow.
Check the chain against itself, and report what the rebuild cost.
It used to compare every answer against the pixel-less pipe, which was the right check while the pipe still owned them – it is what caught the chain composing modules the pipe was suppressing, and it is why the focus exception is evaluated live. That reference is gone with the pipe, so what is left has to test the chain against ITSELF, and only identities that a wrong chain can actually fail are worth printing.
Two of them are:
What it can no longer catch is a chain that is self-consistently wrong: both halves of a partition composing the same wrong subset still add up. That check needed a second implementation, and keeping a whole pipeline alive to be one was the cost this service exists to remove. Under ‘-d dev’; never changes behaviour.
Definition at line 473 of file geometry.c.
References dt_geometry_chain_t::authoritative, dt_geometry_record_t::data, DT_DEBUG_DEV, DT_DEV_TRANSFORM_DIR_ALL, DT_DEV_TRANSFORM_DIR_BACK_EXCL, DT_DEV_TRANSFORM_DIR_BACK_INCL, DT_DEV_TRANSFORM_DIR_FORW_EXCL, DT_DEV_TRANSFORM_DIR_FORW_INCL, dt_free, dt_geometry_backtransform(), dt_geometry_transform(), dt_get_debug_flags(), dt_print(), dt_geometry_record_t::enabled, dt_develop_t::geometry_chain, i, dt_geometry_record_t::instance, dt_geometry_record_t::iop_order, IS_NULL_PTR, dt_geometry_chain_t::missing, names, dt_geometry_record_t::op, dt_geometry_chain_t::processed_height, dt_geometry_chain_t::processed_width, dt_geometry_chain_t::raw_height, dt_geometry_chain_t::raw_width, dt_geometry_chain_t::records, and dt_geometry_record_t::vtable.
Referenced by dt_dev_get_thumbnail_size().
| int dt_geometry_transform | ( | dt_develop_t * | dev, |
| const double | iop_order, | ||
| const int | direction, | ||
| float * | points, | ||
| const size_t | points_count | ||
| ) |
Compose forward over the chain, in place. direction is a DT_DEV_TRANSFORM_DIR_*.
Definition at line 419 of file geometry.c.
References _compose_forward(), dt_geometry_chain_t::authoritative, dt_develop_t::geometry_chain, and IS_NULL_PTR.
Referenced by dt_dev_coordinates_raw_abs_to_image_abs(), dt_dev_distort_transform_gui(), and dt_geometry_self_check().
|
static |
THE ROSTER.
Which modules owe this service a record. It is a hand-maintained list and cannot be anything else: dt_iop_module_t's geometry callbacks are bound through the DEFAULT() macros in common/module_api.h, so EVERY module has a non-NULL, no-op distort_transform() and an identity modify_roi_out(). There is no runtime predicate for "is this a geometry module" to test.
Membership is by source audit (doc/geometry-service.md ยง1): the 15 modules that implement modify_roi_out plus the 11 that implement distort_transform, minus the two that implement neither in a way this service is concerned with.
NOT on the roster, deliberately:
A module added to this list without a geometry_record() implementation keeps the chain non-authoritative forever. That used to be the safe direction because consumers fell back to the pixel-less pipe; there is no fallback now, so it is instead the LOUD direction – sizes come back FALSE and the darkroom cannot lay itself out, with the missing modules named under ‘-d dev’. Wholesale authority is still the rule it enforces: composing some modules from records and the rest from somewhere else interleaves two states, and the result is wrong in a way that looks plausible.
Definition at line 61 of file geometry.c.
Referenced by _on_roster().