Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
geometry.h File Reference

Where things are on the image, answered without a pipeline. More...

#include <glib.h>
#include <stdint.h>
#include "develop/pixelpipe_hb.h"
+ 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_tdt_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_tdt_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.
 

Detailed Description

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 Documentation

◆ dt_geometry_chain_t

The composed geometry of one image, for one dev. GUI thread only.

Definition at line 122 of file geometry.h.

◆ 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.

◆ 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.

Function Documentation

◆ dt_geometry_backtransform()

int dt_geometry_backtransform ( struct dt_develop_t dev,
double  iop_order,
int  direction,
float *  points,
size_t  points_count 
)

◆ dt_geometry_chain_authoritative()

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().

◆ dt_geometry_chain_compose()

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().

◆ dt_geometry_chain_find()

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().

◆ dt_geometry_chain_free()

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().

◆ dt_geometry_chain_generation()

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.

Returns
0 for a chain that has never been built, which no live generation can equal.

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_new()

dt_geometry_chain_t * dt_geometry_chain_new ( void  )

Definition at line 119 of file geometry.c.

Referenced by dt_dev_init(), and main().

◆ dt_geometry_chain_processed_size()

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().

◆ dt_geometry_chain_rebuild()

void dt_geometry_chain_rebuild ( struct dt_develop_t dev)

◆ dt_geometry_module_transform()

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.

Returns
0 when the chain cannot answer – not authoritative, no record, or that module has no transform – in which case points is untouched.

◆ dt_geometry_self_check()

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.

Parameters
chain_mswhat 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:

  • the round trip. transform then backtransform over the whole chain must return the point it started from. Every evaluator's inverse is exercised, and an inverse derived in the wrong frame fails it – which is the question flip's 90-degree orientations pose.
  • the partition. The bounds are a cut of the same ordered list, so composing the two halves must equal composing all of it: FORW_INCL(x) after BACK_EXCL(x) is DIR_ALL, and so is FORW_EXCL(x) after BACK_INCL(x), for x at every module's own iop_order. This is the bound bookkeeping the module GUIs depend on – they ask for their own iop_order, never DIR_ALL – and an off-by-one in _in_bound() drops or repeats exactly one module here.

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().

◆ dt_geometry_transform()

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().