Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
geometry.c
Go to the documentation of this file.
1/*
2 This file is part of Ansel.
3 Copyright (C) 2026 Aurélien Pierre.
4
5 Ansel 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 Ansel 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 Ansel. If not, see <http://www.gnu.org/licenses/>.
17*/
18
20
21#include "common/logging.h"
23#include "develop/dev_history.h"
24#include "develop/develop.h"
25#include "develop/imageop.h"
26#include "system/mem_alloc.h"
27
28#include <math.h>
29#include <string.h>
30
61static const char *const _roster[] = {
62 "rawprepare", "basebuffer", "demosaic", "lens", "ashift", "liquify", "rotatepixels",
63 "scalepixels", "flip", "clipping", "crop", "borders",
64};
65
67{
68 GList *records;
72 gboolean sized;
73
75 gboolean authoritative;
76
79
80 /* What the last rebuild could not get. Kept so shadow mode can name it instead of just
81 * reporting "not ready", which would be true and useless. */
82 GList *missing;
84 /* The dev this chain belongs to. Owned by it and freed with it, so it cannot dangle, and it
85 * is what lets the focus exception below be evaluated the way the pipe evaluates it: live,
86 * at query time, from whatever the GUI currently is. */
88};
89
90static gboolean _on_roster(const char *op)
91{
92 if(IS_NULL_PTR(op)) return FALSE;
93 for(size_t i = 0; i < G_N_ELEMENTS(_roster); i++)
94 if(!strcmp(op, _roster[i])) return TRUE;
95 return FALSE;
96}
97
98static void _record_free(void *ptr)
99{
101 if(IS_NULL_PTR(record)) return;
102 if(!IS_NULL_PTR(record->free_data) && !IS_NULL_PTR(record->data)) record->free_data(record->data);
103 dt_free(record);
104}
105
107{
108 g_list_free_full(chain->records, _record_free);
109 chain->records = NULL;
110 g_list_free_full(chain->missing, dt_free_gpointer);
111 chain->missing = NULL;
112 chain->authoritative = FALSE;
113 chain->sized = FALSE;
114 chain->processed_width = chain->processed_height = 0;
115 // `generation' is deliberately NOT reset here: clearing is a change like any other, and a
116 // consumer holding the old number must see a different one afterwards, not the same one again.
117}
118
120{
121 return (dt_geometry_chain_t *)g_malloc0(sizeof(dt_geometry_chain_t));
122}
123
125{
126 if(IS_NULL_PTR(chain)) return;
127 _chain_clear(chain);
128 dt_free(chain);
129}
130
131static gint _by_iop_order(gconstpointer a, gconstpointer b)
132{
133 const dt_geometry_record_t *ra = (const dt_geometry_record_t *)a;
134 const dt_geometry_record_t *rb = (const dt_geometry_record_t *)b;
135 if(ra->iop_order < rb->iop_order) return -1;
136 if(ra->iop_order > rb->iop_order) return 1;
137 return 0;
138}
139
148static gboolean _suppressed_by_focus(const dt_geometry_chain_t *chain, const dt_geometry_record_t *record)
149{
150 dt_develop_t *const dev = chain->dev;
151 if(IS_NULL_PTR(dev) || !dev->gui_attached) return FALSE;
152
153 dt_iop_module_t *const focused = dev->gui_module;
154 if(IS_NULL_PTR(focused)) return FALSE;
155
156 // the focused module never suppresses itself
157 if(!strcmp(focused->op, record->op) && focused->multi_priority == record->instance) return FALSE;
158
159 // cache bypass is the hint that the focused module is in an editing mode
160 if(!dt_iop_get_cache_bypass(focused)) return FALSE;
161
162 return (focused->operation_tags_filter() & record->operation_tags) != 0;
163}
164
180{
181 dt_iop_roi_t rect = (dt_iop_roi_t){ 0, 0, chain->raw_width, chain->raw_height, 1.0f };
182
183 for(GList *node = g_list_first(chain->records); node; node = g_list_next(node))
184 {
186 record->in = rect;
187
188 if(!IS_NULL_PTR(record->vtable) && !IS_NULL_PTR(record->vtable->map_size)
189 && !_suppressed_by_focus(chain, record))
190 record->vtable->map_size(record->data, &rect, &record->out);
191 else
192 record->out = rect;
193
194 rect = record->out;
195 }
196
197 chain->processed_width = rect.width;
198 chain->processed_height = rect.height;
199 chain->sized = (chain->raw_width > 0 && chain->raw_height > 0);
200}
201
224 const int32_t history_end, const void **params)
225{
226 if(module->flags() & IOP_FLAGS_NO_HISTORY_STACK)
227 {
228 *params = module->default_params;
229 return module->default_enabled;
230 }
231
232 *params = module->default_params;
233 gboolean enabled = module->default_enabled;
234
235 for(GList *item = g_list_nth(dev->history, history_end - 1); item; item = g_list_previous(item))
236 {
237 const dt_dev_history_item_t *const hist = (const dt_dev_history_item_t *)item->data;
238 if(IS_NULL_PTR(hist) || hist->module != module) continue;
239 *params = hist->params;
240 enabled = hist->enabled;
241 break;
242 }
243
244 return enabled;
245}
246
248{
249 if(IS_NULL_PTR(dev) || IS_NULL_PTR(dev->geometry_chain)) return;
250
252 _chain_clear(chain);
253 chain->dev = dev;
254
255 int32_t raw_width = 0;
256 int32_t raw_height = 0;
257 if(!dt_dev_geometry_get_raw_size(dev, &raw_width, &raw_height)) return;
258 chain->raw_width = raw_width;
259 chain->raw_height = raw_height;
260
261 gboolean complete = TRUE;
262
263 /* Resolve each module the way the pipe will, from HISTORY -- see _resolve_from_history(). The
264 * read lock is what dt_dev_pixelpipe_change() takes for the same walk; it is same-thread
265 * recursive, so a caller that already holds it (a history commit republishing geometry) is
266 * fine. */
268 const int32_t history_end = dt_dev_get_history_end_ext(dev);
269
270 for(GList *node = g_list_first(dev->iop); node; node = g_list_next(node))
271 {
272 dt_iop_module_t *module = (dt_iop_module_t *)node->data;
273 if(IS_NULL_PTR(module)) continue;
274
275 dt_geometry_record_t *record = (dt_geometry_record_t *)g_malloc0(sizeof(dt_geometry_record_t));
276 if(IS_NULL_PTR(record)) continue;
277
278 g_strlcpy(record->op, module->op, sizeof(record->op));
279 record->instance = module->multi_priority;
280 record->iop_order = module->iop_order;
281 record->operation_tags = module->operation_tags();
282
283 const void *params = NULL;
284 record->enabled = _resolve_from_history(dev, module, history_end, &params);
285
286 /* A record exists for EVERY module, enabled or not, because the fold this mirrors writes
287 * per-piece dimensions for disabled pieces too and consumers read them. A disabled module
288 * simply publishes nothing and is identity.
289 *
290 * A module that publishes nothing is also correct for the ~80 that do not touch geometry.
291 * A ROSTER module that publishes nothing is a gap, and the chain must not be trusted until
292 * it closes -- but only while it is ENABLED: a disabled lens owes this service nothing, and
293 * asking it for a record would build a lensfun modifier for a correction nobody applies. */
294 gboolean published = FALSE;
295 if(record->enabled && !IS_NULL_PTR(module->geometry_record))
296 published = module->geometry_record(module, params, record);
297
298 if(record->enabled && !published && _on_roster(module->op))
299 {
300 complete = FALSE;
301 chain->missing = g_list_prepend(chain->missing,
302 g_strdup_printf("%s.%d", module->op, module->multi_priority));
303 }
304
305 chain->records = g_list_insert_sorted(chain->records, record, _by_iop_order);
306 }
307
309
310 _fold_sizes(chain);
311 chain->authoritative = complete && chain->sized;
312
313 /* Last, so a consumer that samples the generation and then reads the chain cannot pair a new
314 * number with geometry still being folded -- this all runs on the GUI thread, but the ordering
315 * is what makes the key mean "everything below is settled". */
316 chain->generation++;
317}
318
320{
321 return IS_NULL_PTR(chain) ? 0 : chain->generation;
322}
323
325{
326 return !IS_NULL_PTR(chain) && chain->authoritative;
327}
328
330{
331 if(IS_NULL_PTR(chain) || !chain->sized) return FALSE;
332 if(!IS_NULL_PTR(width)) *width = chain->processed_width;
334 return TRUE;
335}
336
338 const int instance)
339{
340 if(IS_NULL_PTR(chain) || IS_NULL_PTR(op)) return NULL;
341 for(const GList *node = g_list_first(chain->records); node; node = g_list_next(node))
342 {
343 const dt_geometry_record_t *record = (const dt_geometry_record_t *)node->data;
344 if(!strcmp(record->op, op) && record->instance == instance) return record;
345 }
346 return NULL;
347}
348
357static gboolean _in_bound(const dt_geometry_record_t *record, const double iop_order, const int direction)
358{
359 switch(direction)
360 {
361 case DT_DEV_TRANSFORM_DIR_FORW_INCL: return record->iop_order >= iop_order;
362 case DT_DEV_TRANSFORM_DIR_FORW_EXCL: return record->iop_order > iop_order;
363 case DT_DEV_TRANSFORM_DIR_BACK_INCL: return record->iop_order <= iop_order;
364 case DT_DEV_TRANSFORM_DIR_BACK_EXCL: return record->iop_order < iop_order;
366 default: return TRUE;
367 }
368}
369
372static int _compose_forward(dt_geometry_chain_t *chain, const double iop_order, const int direction,
373 float *points, const size_t points_count)
374{
375 for(GList *node = g_list_first(chain->records); node; node = g_list_next(node))
376 {
378 if(IS_NULL_PTR(record->vtable) || IS_NULL_PTR(record->vtable->transform)) continue;
379 if(!_in_bound(record, iop_order, direction)) continue;
380 if(_suppressed_by_focus(chain, record)) continue;
381 record->vtable->transform(record->data, record, chain, points, points_count);
382 }
383 return 1;
384}
385
386int dt_geometry_module_transform(dt_develop_t *dev, const dt_iop_module_t *module, float *points,
387 const size_t points_count)
388{
389 if(IS_NULL_PTR(dev) || IS_NULL_PTR(module) || IS_NULL_PTR(points)) return 0;
390
391 dt_geometry_chain_t *const chain = dev->geometry_chain;
392 if(IS_NULL_PTR(chain) || !chain->authoritative) return 0;
393
394 dt_geometry_record_t *record = NULL;
395 for(GList *node = g_list_first(chain->records); node; node = g_list_next(node))
396 {
397 dt_geometry_record_t *const candidate = (dt_geometry_record_t *)node->data;
398 if(!strcmp(candidate->op, module->op) && candidate->instance == module->multi_priority)
399 {
400 record = candidate;
401 break;
402 }
403 }
404
405 if(IS_NULL_PTR(record) || !record->enabled) return 0;
406 if(IS_NULL_PTR(record->vtable) || IS_NULL_PTR(record->vtable->transform)) return 0;
407 if(_suppressed_by_focus(chain, record)) return 0;
408
409 return record->vtable->transform(record->data, record, chain, points, points_count);
410}
411
412int dt_geometry_chain_compose(dt_geometry_chain_t *chain, const double iop_order, const int direction,
413 float *points, const size_t points_count)
414{
415 if(IS_NULL_PTR(chain) || IS_NULL_PTR(points)) return 0;
416 return _compose_forward(chain, iop_order, direction, points, points_count);
417}
418
419int dt_geometry_transform(dt_develop_t *dev, const double iop_order, const int direction, float *points,
420 const size_t points_count)
421{
422 if(IS_NULL_PTR(dev) || IS_NULL_PTR(dev->geometry_chain) || IS_NULL_PTR(points)) return 0;
424 if(!chain->authoritative) return 0;
425
426 return _compose_forward(chain, iop_order, direction, points, points_count);
427}
428
429int dt_geometry_backtransform(dt_develop_t *dev, const double iop_order, const int direction, float *points,
430 const size_t points_count)
431{
432 if(IS_NULL_PTR(dev) || IS_NULL_PTR(dev->geometry_chain) || IS_NULL_PTR(points)) return 0;
434 if(!chain->authoritative) return 0;
435
436 for(GList *node = g_list_last(chain->records); node; node = g_list_previous(node))
437 {
439 if(IS_NULL_PTR(record->vtable) || IS_NULL_PTR(record->vtable->backtransform)) continue;
440 if(!_in_bound(record, iop_order, direction)) continue;
441 if(_suppressed_by_focus(chain, record)) continue;
442 record->vtable->backtransform(record->data, record, chain, points, points_count);
443 }
444 return 1;
445}
446
473void dt_geometry_self_check(dt_develop_t *dev, const double chain_ms)
474{
475 if(IS_NULL_PTR(dev) || IS_NULL_PTR(dev->geometry_chain)) return;
476 if(!(dt_get_debug_flags() & DT_DEBUG_DEV)) return;
477
478 const dt_geometry_chain_t *chain = dev->geometry_chain;
479
480 if(!chain->authoritative)
481 {
482 /* Name the gap. "Not ready" is true and useless; the list below is the actual, per-image
483 * work remaining, measured rather than taken from the source audit -- which is how a module
484 * that the audit missed, or one that only becomes enabled on some images, gets found. */
485 if(IS_NULL_PTR(chain->missing))
486 {
487 dt_print(DT_DEBUG_DEV, "[geometry] chain not authoritative: no usable raw geometry yet\n");
488 return;
489 }
490
491 gchar **names = (gchar **)g_malloc0((g_list_length(chain->missing) + 1) * sizeof(gchar *));
492 if(IS_NULL_PTR(names)) return;
493 int i = 0;
494 for(const GList *node = g_list_first(chain->missing); node; node = g_list_next(node))
495 names[i++] = (gchar *)node->data;
496 gchar *joined = g_strjoinv(", ", names);
497 dt_free(names); // the strings belong to chain->missing; only the vector is ours
498
499 dt_print(DT_DEBUG_DEV, "[geometry] chain not authoritative: %d roster module(s) without a record: %s\n",
500 g_list_length(chain->missing), joined);
501 dt_free(joined);
502 return;
503 }
504
505 /* Five points spanning the raw frame. The tolerance is half a pixel throughout: every identity
506 * below composes the same evaluators in the same order, so they should agree exactly, but the
507 * fold's rects reach them as ints and a rounding difference of that size is not a defect worth
508 * failing on. Anything larger is real and is printed with the point. */
509 const float w = (float)chain->raw_width;
510 const float h = (float)chain->raw_height;
511 const float origin[10] = { 0.f, 0.f, w, 0.f, 0.f, h, w, h, 0.5f * w, 0.5f * h };
512
513 float roundtrip[10];
514 memcpy(roundtrip, origin, sizeof(roundtrip));
515 if(!dt_geometry_transform(dev, 0.0, DT_DEV_TRANSFORM_DIR_ALL, roundtrip, 5)) return;
517
518 int not_identity = 0;
519 for(int i = 0; i < 5; i++)
520 {
521 if(fabsf(roundtrip[2 * i] - origin[2 * i]) > 0.5f
522 || fabsf(roundtrip[2 * i + 1] - origin[2 * i + 1]) > 0.5f)
523 {
525 "[geometry] ROUND-TRIP DIVERGENCE at probe %d: (%.2f, %.2f) came back as (%.2f, %.2f)\n", i,
526 origin[2 * i], origin[2 * i + 1], roundtrip[2 * i], roundtrip[2 * i + 1]);
527 not_identity++;
528 }
529 }
530
531 // The whole chain, once: what both halves of every partition below must add up to.
532 float whole[2] = { 0.37f * w, 0.61f * h };
534
537 static const char *const cut_names[2] = { "BACK_EXCL|FORW_INCL", "BACK_INCL|FORW_EXCL" };
538 int bound_diverged = 0;
539
540 for(const GList *node = g_list_first(chain->records); node; node = g_list_next(node))
541 {
542 const dt_geometry_record_t *const record = (const dt_geometry_record_t *)node->data;
543 if(!record->enabled || IS_NULL_PTR(record->vtable)) continue;
544
545 for(int c = 0; c < 2; c++)
546 {
547 float cut[2] = { 0.37f * w, 0.61f * h };
548 dt_geometry_transform(dev, record->iop_order, back[c], cut, 1);
549 dt_geometry_transform(dev, record->iop_order, forw[c], cut, 1);
550
551 if(fabsf(cut[0] - whole[0]) > 0.5f || fabsf(cut[1] - whole[1]) > 0.5f)
552 {
554 "[geometry] BOUND DIVERGENCE at %s.%d %s: cut (%.2f, %.2f), whole (%.2f, %.2f)\n",
555 record->op, record->instance, cut_names[c], cut[0], cut[1], whole[0], whole[1]);
556 bound_diverged++;
557 }
558 }
559 }
560
561 if(not_identity || bound_diverged)
562 dt_print(DT_DEBUG_DEV, "[geometry] size %dx%d, %d/5 round-trips and %d bound cut(s) diverge\n",
563 chain->processed_width, chain->processed_height, not_identity, bound_diverged);
564 else
565 dt_print(DT_DEBUG_DEV, "[geometry] consistent: size %dx%d, 5/5 round-trips, all bound cuts\n",
566 chain->processed_width, chain->processed_height);
567
568 /* The cost, which is the reason this service exists. Its predecessor's counterpart is in the
569 * git history of this file and in doc/geometry-service.md: `-d perf's "pipeline resync with
570 * history ... for pipe virtual-preview", 0.10 to 0.33 s, on this same thread. */
571 dt_print(DT_DEBUG_DEV, "[geometry] chain rebuilt in %.2f ms\n", chain_ms);
572}
573
574// clang-format off
575// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
576// vim: shiftwidth=2 expandtab tabstop=2 cindent
577// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
578// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
gboolean dt_dev_geometry_get_raw_size(const dt_develop_t *dev, int32_t *width, int32_t *height)
int32_t dt_dev_get_history_end_ext(struct dt_develop_t *dev)
Get the current history end index (GUI perspective).
Definition develop.c:1899
@ DT_DEV_TRANSFORM_DIR_BACK_EXCL
Definition develop.h:111
@ DT_DEV_TRANSFORM_DIR_BACK_INCL
Definition develop.h:110
@ DT_DEV_TRANSFORM_DIR_FORW_INCL
Definition develop.h:108
@ DT_DEV_TRANSFORM_DIR_ALL
Definition develop.h:107
@ DT_DEV_TRANSFORM_DIR_FORW_EXCL
Definition develop.h:109
GHashTable * names
GtkWidget* -> the name the application knows it by.
gboolean enabled
–doc was passed on the command line
static int dt_pthread_rwlock_unlock(dt_pthread_rwlock_t *rwlock) RELEASE_GENERIC(rwlock) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:217
static int dt_pthread_rwlock_rdlock(dt_pthread_rwlock_t *rwlock) ACQUIRE_SHARED(rwlock) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:267
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?
Definition geometry.c:148
static const char *const _roster[]
Definition geometry.c:61
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 geometry.c:337
dt_geometry_chain_t * dt_geometry_chain_new(void)
Definition geometry.c:119
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.
Definition geometry.c:223
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 m...
Definition geometry.c:412
static gint _by_iop_order(gconstpointer a, gconstpointer b)
Definition geometry.c:131
static void _record_free(void *ptr)
Definition geometry.c:98
static gboolean _on_roster(const char *op)
Definition geometry.c:90
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 geometry.c:429
static void _chain_clear(dt_geometry_chain_t *chain)
Definition geometry.c:106
gboolean dt_geometry_chain_authoritative(const dt_geometry_chain_t *chain)
Can this chain answer questions yet?
Definition geometry.c:324
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 ge...
Definition geometry.c:319
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.
Definition geometry.c:473
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 geometry.c:329
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?
Definition geometry.c:357
void dt_geometry_chain_free(dt_geometry_chain_t *chain)
Definition geometry.c:124
static void _fold_sizes(dt_geometry_chain_t *chain)
Fold every record's map_size in pipe order, at full resolution and scale 1.
Definition geometry.c:179
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 geometry.c:419
int dt_geometry_module_transform(dt_develop_t *dev, const dt_iop_module_t *module, float *points, const size_t points_count)
Definition geometry.c:386
void dt_geometry_chain_rebuild(dt_develop_t *dev)
Rebuild the chain from the dev's current modules and history. GUI thread only.
Definition geometry.c:247
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()....
Definition geometry.c:372
Where things are on the image, answered without a pipeline.
gboolean dt_iop_get_cache_bypass(dt_iop_module_t *module)
Definition imageop.c:1664
@ IOP_FLAGS_NO_HISTORY_STACK
Definition imageop.h:193
@ DT_DEBUG_DEV
Definition logging.h:53
int32_t dt_get_debug_flags(void)
Definition darktable.c:2085
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
static void dt_free_gpointer(gpointer ptr)
g_free() one pointer, with the signature GDestroyNotify wants.
Definition mem_alloc.h:184
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
unsigned __int64 uint64_t
Definition strptime.c:75
dt_iop_params_t * params
Definition dev_history.h:54
struct dt_iop_module_t *gboolean enabled
Definition dev_history.h:52
int32_t gui_attached
Definition develop.h:167
GList * iop
Definition develop.h:269
struct dt_iop_module_t * gui_module
Definition develop.h:170
dt_pthread_rwlock_t history_mutex
Definition develop.h:247
GList * history
Definition develop.h:259
struct dt_geometry_chain_t * geometry_chain
Definition develop.h:491
dt_develop_t * dev
Definition geometry.c:87
int32_t processed_height
Definition geometry.c:71
uint64_t generation
Definition geometry.c:78
int32_t processed_width
Definition geometry.c:71
gboolean authoritative
Definition geometry.c:75
One module instance's contribution, as data.
Definition geometry.h:99
dt_iop_roi_t in
Definition geometry.h:117
const dt_geometry_vtable_t * vtable
Definition geometry.h:110
void(* free_data)(void *data)
Definition geometry.h:112
dt_iop_roi_t out
Definition geometry.h:118
int(* backtransform)(const void *data, const struct dt_geometry_record_t *const record, struct dt_geometry_chain_t *chain, float *points, size_t points_count)
The inverse of transform.
Definition geometry.h:86
void(* map_size)(const void *data, const dt_iop_roi_t *const in, dt_iop_roi_t *out)
Full-resolution input rect -> output rect. Mirrors modify_roi_out() at scale 1.
Definition geometry.h:77
int(* transform)(const void *data, const struct dt_geometry_record_t *const record, struct dt_geometry_chain_t *chain, float *points, size_t points_count)
Image coordinates in -> image coordinates out, in place, points_count pairs. ctx composes the sub-cha...
Definition geometry.h:82
dt_dev_operation_t op
Definition imageop.h:259
Region of interest passed through the pixelpipe.
Definition format.h:49
double width