Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
masks_debug.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
19/* dt_masks_debug_write_png() is NOT here: compositing the GUI overlay needs cairo, and
20 * src/develop is held toolkit-free by tools/check_module_boundaries.sh -- the pixel engine has
21 * to stay portable. It lives beside the overlay code it calls, in masks/masks_gui.c. What is
22 * left in this file is the toolkit-free half: the rasteriser and the outline dump. */
23
24#include "develop/masks_debug.h"
25
26#include "common/logging.h"
27#include "develop/develop.h"
29#include "develop/masks.h"
30#include "develop/masks_gui.h"
33#include "system/mem_alloc.h"
34
35#include <math.h>
36
37float *dt_masks_debug_rasterise(dt_develop_t *dev, dt_masks_form_t *form, const int width, const int height)
38{
39 if(IS_NULL_PTR(dev) || IS_NULL_PTR(form) || width <= 0 || height <= 0) return NULL;
40
41 int32_t raw_width = 0;
42 int32_t raw_height = 0;
43 if(!dt_dev_geometry_get_raw_size(dev, &raw_width, &raw_height) || raw_width <= 0 || raw_height <= 0)
44 {
45 dt_print(DT_DEBUG_ALWAYS, "[masks debug] the dev has no raw geometry; nothing to rasterise against\n");
46 return NULL;
47 }
48
49 float *const buffer = dt_calloc_align_float((size_t)width * height);
50 if(IS_NULL_PTR(buffer)) return NULL;
51
52 /* A pipe with no nodes: dt_dev_distort_transform_plus() then walks an empty list, so the
53 * composition is the identity and the shape lands on raw coordinates. That is deliberate --
54 * a geometry regression must not move because some other module's distortion changed. The
55 * three fields dt_masks_distort_for_pipe() reads are the only ones that matter here, plus
56 * `forms', which is how a group resolves its children (they are borrowed, not referenced:
57 * this pipe does not outlive the call). */
58 dt_dev_pixelpipe_t pipe = { 0 };
59 pipe.iwidth = raw_width;
60 pipe.iheight = raw_height;
62 pipe.forms = dev->forms;
63
64 dt_dev_pixelpipe_iop_t piece = { 0 };
65 dt_iop_module_t module = { 0 };
66 module.dev = dev;
67 module.iop_order = 0.0;
68
69 const dt_iop_roi_t roi = { .x = 0, .y = 0, .width = width, .height = height,
70 .scale = (double)width / (double)raw_width };
71 dt_iop_roi_t touched = { 0 };
72
73 const dt_masks_raster_result_t result
74 = dt_masks_get_mask_roi(&module, &pipe, &piece, form, &roi, buffer, &touched);
75
76 if(result == DT_MASKS_RASTER_ERROR)
77 {
78 dt_print(DT_DEBUG_ALWAYS, "[masks debug] rasterising '%s' failed\n", form->name);
79 dt_free_align(buffer);
80 return NULL;
81 }
82
83 // EMPTY is a result, not a failure: the caller gets the zeroed buffer it describes.
84 return buffer;
85}
86
87gboolean dt_masks_debug_write_outline_csv(dt_develop_t *dev, dt_masks_form_t *form, const char *path)
88{
89 if(IS_NULL_PTR(dev) || IS_NULL_PTR(form) || IS_NULL_PTR(path)) return FALSE;
90
91 float *points = NULL, *border = NULL;
92 int points_count = 0, border_count = 0;
93 dt_masks_skip_range_t *skips = NULL;
94 int skip_count = 0;
95
96 const dt_masks_raster_result_t st = dt_masks_get_points_border(dev, form, &points, &points_count,
97 &border, &border_count,
98 &skips, &skip_count, 0, NULL);
99 dt_print(DT_DEBUG_ALWAYS, "[masks debug] outline: status=%d points=%d border=%d\n", st, points_count, border_count);
100 if(st == DT_MASKS_RASTER_ERROR) return FALSE;
101
102 FILE *f = fopen(path, "w");
103 if(IS_NULL_PTR(f))
104 {
108 return FALSE;
109 }
110
111 fprintf(f, "# points=%d border=%d skips=%d\n", points_count, border_count, skip_count);
112 for(int i = 0; i < skip_count; i++)
113 fprintf(f, "# skip %d..%d\n", skips[i].jump_from, skips[i].resume_at);
114 fprintf(f, "i,px,py,bx,by\n");
115 const int n = MIN(points_count, border_count);
116 for(int i = 0; i < n; i++)
117 fprintf(f, "%d,%.4f,%.4f,%.4f,%.4f\n", i, points[i * 2], points[i * 2 + 1],
118 border[i * 2], border[i * 2 + 1]);
119 fclose(f);
120
124 return TRUE;
125}
126
127
128// clang-format off
129// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
130// vim: shiftwidth=2 expandtab tabstop=2 cindent
131// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
132// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
const float f
gboolean dt_dev_geometry_get_raw_size(const dt_develop_t *dev, int32_t *width, int32_t *height)
@ DT_DEBUG_ALWAYS
Definition logging.h:49
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
dt_masks_raster_result_t dt_masks_get_points_border(dt_develop_t *develop, dt_masks_form_t *mask_form, float **point_buffer, int *point_count, float **border_buffer, int *border_count, dt_masks_skip_range_t **border_skips, int *border_skip_count, int source, dt_iop_module_t *module)
Definition masks.c:238
dt_masks_raster_result_t dt_masks_get_mask_roi(const dt_iop_module_t *const module, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *const piece, dt_masks_form_t *const form, const dt_iop_roi_t *roi, float *buffer, dt_iop_roi_t *touched)
Definition masks.c:1642
dt_masks_raster_result_t
What a rasterisation attempt produced.
Definition masks.h:313
@ DT_MASKS_RASTER_ERROR
Definition masks.h:316
gboolean dt_masks_debug_write_outline_csv(dt_develop_t *dev, dt_masks_form_t *form, const char *path)
Write the shape's outline buffers to path as CSV: index, centreline, border.
Definition masks_debug.c:87
float * dt_masks_debug_rasterise(dt_develop_t *dev, dt_masks_form_t *form, const int width, const int height)
Rasterise form into a freshly allocated width*height float buffer, 0..1.
Definition masks_debug.c:37
Render a mask's rasterisation and its GUI overlay to an image file, headlessly.
The per-shape function table, private to the masks implementation.
The interactive half of the masks subsystem: the editing state (dt_masks_form_gui_t),...
static float * dt_calloc_align_float(size_t pixels)
dt_alloc_align_float() followed by a zero fill.
Definition mem_alloc.h:241
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
Definition mem_alloc.h:214
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
#define dt_pixelpipe_cache_free_align(mem)
int mask_rasterization_step
Rasterisation step for drawn masks, in image pixels. Always >= 1.
GList * forms
Definition develop.h:304
Region of interest passed through the pixelpipe.
Definition format.h:49
char name[128]
Definition masks.h:277
One cut in a shape's border outline: while walking the border buffer forward, on reaching index jump_...
typedef double((*spd)(unsigned long int wavelength, double TempK))
#define MIN(a, b)
Definition thinplate.c:32