![]() |
Ansel 0.0
A darktable fork - bloat + design vision
|
Where things are on the image, answered without a pipeline. More...
Include dependency graph for geometry.h:
This graph shows which files directly or indirectly include this file:Go to the source code of this file.
Data Structures | |
| struct | dt_geometry_vtable_t |
| A module's geometry, evaluated. Pure functions of the record's own data. More... | |
| struct | dt_geometry_record_t |
| One module instance's contribution, as data. More... | |
Typedefs | |
| typedef struct dt_geometry_vtable_t | dt_geometry_vtable_t |
| A module's geometry, evaluated. Pure functions of the record's own data. | |
| typedef struct dt_geometry_record_t | dt_geometry_record_t |
| One module instance's contribution, as data. | |
| typedef struct dt_geometry_chain_t | dt_geometry_chain_t |
| The composed geometry of one image, for one dev. GUI thread only. | |
Functions | |
| dt_geometry_chain_t * | dt_geometry_chain_new (void) |
| void | dt_geometry_chain_free (dt_geometry_chain_t *chain) |
| void | dt_geometry_chain_rebuild (struct dt_develop_t *dev) |
| Rebuild the chain from the dev's current modules and history. GUI thread only. | |
| gboolean | dt_geometry_chain_authoritative (const dt_geometry_chain_t *chain) |
| Can this chain answer questions yet? | |
| 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_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, int instance) |
| One module instance's record, or NULL. Use it for that module's own in/out dims. | |
| int | dt_geometry_transform (struct dt_develop_t *dev, double iop_order, int direction, float *points, size_t points_count) |
Compose forward over the chain, in place. direction is a DT_DEV_TRANSFORM_DIR_*. | |
| int | dt_geometry_backtransform (struct dt_develop_t *dev, double iop_order, int direction, float *points, size_t points_count) |
| Compose backward over the chain, in place. | |
| int | dt_geometry_module_transform (struct dt_develop_t *dev, const struct dt_iop_module_t *module, float *points, size_t points_count) |
| Apply ONE module's own transform, and nothing else. | |
| 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. | |
| void | dt_geometry_self_check (struct dt_develop_t *dev, double chain_ms) |
| Check the chain against itself, and report what the rebuild cost. | |
Where things are on the image, answered without a pipeline.
The GUI constantly needs two geometric facts: how big the developed image is, and where a point on it lands after the distorting modules have had their say. Both used to be answered by a complete, pixel-less clone of all ~95 IOP modules – their history committed into real pipeline nodes, resynchronised on the GUI thread at every history commit for 0.10 to 0.33 s. It rendered nothing. It existed only to be walked.
This module is the data that walk actually consumed. Each module that changes geometry publishes a small record – its transform as values, not as a node – and the GUI composes sizes and coordinates from the ordered list. See doc/geometry-service.md for the decision, the survey behind it, and what each tranche moved; the traps section there is not optional reading.
THREADING. This is GUI-thread state and takes no locks. Nothing else may touch it. The pixel pipelines keep their own piece-based modify_roi and distort callbacks for rendering, and the two must never be crossed: a worker that needs geometry asks its own pieces. That separation is the whole design: the GUI's copy of the module stack is gone, not shared.
Definition in file geometry.h.
| typedef struct dt_geometry_chain_t dt_geometry_chain_t |
The composed geometry of one image, for one dev. GUI thread only.
Definition at line 122 of file geometry.h.
| typedef struct dt_geometry_record_t dt_geometry_record_t |
One module instance's contribution, as data.
There is a record for EVERY enabled module, not only the geometric ones: consumers ask this list for their own module's input and output dimensions, and ‘graduatednd’ – which has no geometry callbacks at all – is one of them. A module with no geometry gets a record with a NULL vtable, which the size fold treats as identity and the walkers skip.
| typedef struct dt_geometry_vtable_t dt_geometry_vtable_t |
A module's geometry, evaluated. Pure functions of the record's own data.
Every entry may be NULL, which means "identity for this operation": a module that resizes but does not move points (demosaic's downsample) has a map_size and no transform, and a module that is only ever asked for its dimensions (graduatednd) has neither.
These evaluators are the SAME code the module's own distort_transform()/modify_roi_out() run on the pixel pipe – one shared static helper per module, called from both sides. That rule is not stylistic: two derivations of the same geometry drift, and the drift shows up as an overlay that no longer sits on the thing it describes, months later, on one image.
| int dt_geometry_backtransform | ( | struct dt_develop_t * | dev, |
| double | iop_order, | ||
| int | direction, | ||
| float * | points, | ||
| 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, | ||
| 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 | ( | struct dt_develop_t * | dev, |
| const struct dt_iop_module_t * | module, | ||
| float * | points, | ||
| size_t | points_count | ||
| ) |
Apply ONE module's own transform, and nothing else.
No direction bound expresses this: FORW_INCL at a module's own iop_order includes everything after it as well. iop/ashift.c wants exactly its own homography applied to the corners of its input, to work out where its output lands, and reached it by resolving its piece and calling its own distort_transform() through the module vtable.
Honours the focused-module exception like every other composition here, so a module suppressed by whatever is being edited contributes nothing, exactly as it would in a full walk.
points is untouched. | void dt_geometry_self_check | ( | dt_develop_t * | dev, |
| const double | chain_ms | ||
| ) |
Check the chain against itself, and report what the rebuild cost.
What the shadow harness became. It compared every answer against the pipe while the pipe still owned them; with the pipe gone the only checks left are identities the chain must satisfy on its own – the round trip, and the bound partition. See the comment on the definition for what that does and does not still catch. Under ‘-d dev’; never changes behaviour.
| chain_ms | what the rebuild cost. |
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 | ( | struct dt_develop_t * | dev, |
| double | iop_order, | ||
| int | direction, | ||
| float * | points, | ||
| 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().