Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
rotatepixels.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2014 Pascal de Bruijn.
4 Copyright (C) 2014, 2016 Roman Lebedev.
5 Copyright (C) 2014, 2016, 2019 Tobias Ellinghaus.
6 Copyright (C) 2017 Heiko Bauke.
7 Copyright (C) 2017 luzpaz.
8 Copyright (C) 2018, 2020-2021, 2023, 2025-2026 Aurélien PIERRE.
9 Copyright (C) 2018 Edgardo Hoszowski.
10 Copyright (C) 2018 Maurizio Paglia.
11 Copyright (C) 2018-2022 Pascal Obry.
12 Copyright (C) 2018 rawfiner.
13 Copyright (C) 2019-2020 Aldric Renaudin.
14 Copyright (C) 2019 Andreas Schneider.
15 Copyright (C) 2020 Diederik Ter Rahe.
16 Copyright (C) 2020-2021 Ralf Brown.
17 Copyright (C) 2022 Martin Bařinka.
18 Copyright (C) 2022 Philipp Lutz.
19
20 darktable is free software: you can redistribute it and/or modify
21 it under the terms of the GNU General Public License as published by
22 the Free Software Foundation, either version 3 of the License, or
23 (at your option) any later version.
24
25 darktable is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 GNU General Public License for more details.
29
30 You should have received a copy of the GNU General Public License
31 along with darktable. If not, see <http://www.gnu.org/licenses/>.
32*/
33
34#ifdef HAVE_CONFIG_H
35#include "config.h"
36#endif
37#include "bauhaus/bauhaus.h"
39#include "common/math.h"
40#include "develop/develop.h"
41#include "develop/imageop.h"
42#include "develop/tiling.h"
43#include "gui/gtk.h"
44#include "iop/iop_api.h"
45
46#include <stdlib.h>
47
49
53
59
61{
62 uint32_t rx, ry; // rotation center
63 float m[4]; // rotation matrix
65
67
68// helper to count corners in for loops:
69static void get_corner(const float *aabb, const int i, float *p)
70{
71 for(int k = 0; k < 2; k++) p[k] = aabb[2 * ((i >> k) & 1) + k];
72}
73
74static void adjust_aabb(const float *p, float *aabb)
75{
76 aabb[0] = fminf(aabb[0], p[0]);
77 aabb[1] = fminf(aabb[1], p[1]);
78 aabb[2] = fmaxf(aabb[2], p[0]);
79 aabb[3] = fmaxf(aabb[3], p[1]);
80}
81
82
83const char *name()
84{
85 return C_("modulename", "rotate pixels");
86}
87
93
95{
97}
98
100{
101 return IOP_TAG_DISTORT;
102}
103
105{
106 return IOP_CS_RGB;
107}
108
109const char **description(struct dt_iop_module_t *self)
110{
111 return dt_iop_set_description(self,
112 _("internal module to setup technical specificities of raw sensor.\n\n"
113 "you should not touch values here !"),
114 NULL, NULL, NULL, NULL);
115}
116
117
119static void transform(const dt_dev_pixelpipe_iop_t *const piece, const float scale, const float *const x,
120 float *o)
121{
123
124 float pi[2] = { x[0] - d->rx * scale, x[1] - d->ry * scale };
125
126 mul_mat_vec_2(d->m, pi, o);
127}
128
129
131static void backtransform(const dt_dev_pixelpipe_iop_t *const piece, const float scale, const float *const x,
132 float *o)
133{
135
136 float rt[] = { d->m[0], -d->m[1], -d->m[2], d->m[3] };
137 mul_mat_vec_2(rt, x, o);
138
139 o[0] += d->rx * scale;
140 o[1] += d->ry * scale;
141}
142
144 float *const restrict points, size_t points_count)
145{
146 const float scale = piece->buf_in.scale;
147 __OMP_PARALLEL_FOR_SIMD__(if(points_count > 100) aligned(points:64))
148 for(size_t i = 0; i < points_count * 2; i += 2)
149 {
150 float pi[2], po[2];
151
152 pi[0] = points[i];
153 pi[1] = points[i + 1];
154
155 transform(piece, scale, pi, po);
156
157 points[i] = po[0];
158 points[i + 1] = po[1];
159 }
160
161 return 1;
162}
163
165 float *const restrict points, size_t points_count)
166{
167 const float scale = piece->buf_in.scale;
168 __OMP_PARALLEL_FOR_SIMD__(if(points_count > 100) aligned(points:64))
169 for(size_t i = 0; i < points_count * 2; i += 2)
170 {
171 float pi[2], po[2];
172
173 pi[0] = points[i];
174 pi[1] = points[i + 1];
175
176 backtransform(piece, scale, pi, po);
177
178 points[i] = po[0];
179 points[i + 1] = po[1];
180 }
181
182 return 1;
183}
184
185void distort_mask(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece,
186 const float *const in, float *const out, const dt_iop_roi_t *const roi_in,
187 const dt_iop_roi_t *const roi_out)
188{
189 (void)pipe;
190 // TODO
191 memset(out, 0, sizeof(float) * roi_out->width * roi_out->height);
192 fprintf(stderr, "TODO: implement %s() in %s\n", __FUNCTION__, __FILE__);
193}
194
195// 1st pass: how large would the output be, given this input roi?
196// this is always called with the full buffer before processing.
198 dt_iop_roi_t *roi_out,
199 const dt_iop_roi_t *const roi_in)
200{
202
203 *roi_out = *roi_in;
204
205 /*
206 * Think of input image as:
207 * 1. Square, containing:
208 * 3. 4x Right triangles (two pairs), located in the Edges of square
209 * 2. Rectangle (rotated 45 degrees), located in between triangles.
210 *
211 * Therefore, output image dimensions, that are sizes of inner Rectangle
212 * can be found using Pythagorean theorem.
213 *
214 * Not precise pseudographics: (width == height + 1)
215 * height
216 * _ -------
217 * | |1 /\2|
218 * ty | | y \|
219 * _ |/ /| width
220 * |\x / |
221 * |2\/ 1|
222 * -------
223 */
224
225 const float scale = roi_in->scale;
226 const float T = (float)d->ry * scale;
227
228 const float y = sqrtf(2.0f * T * T),
229 x = sqrtf(2.0f * ((float)roi_in->width - T) * ((float)roi_in->width - T));
230
232 const float IW = (float)interpolation->width * scale;
233
234 roi_out->width = y - IW;
235 roi_out->height = x - IW;
236
237 roi_out->width = MAX(0, roi_out->width & ~1);
238 roi_out->height = MAX(0, roi_out->height & ~1);
239}
240
241// 2nd pass: which roi would this operation need as input to fill the given output region?
243 const dt_iop_roi_t *const roi_out,
244 dt_iop_roi_t *roi_in)
245{
246 *roi_in = *roi_out;
247
248 const float scale = roi_in->scale;
249
250 dt_boundingbox_t aabb = { roi_out->x, roi_out->y, roi_out->x + roi_out->width, roi_out->y + roi_out->height };
251
252 dt_boundingbox_t aabb_in = { INFINITY, INFINITY, -INFINITY, -INFINITY };
253
254 for(int c = 0; c < 4; c++)
255 {
256 float p[2], o[2];
257
258 // get corner points of roi_out
259 get_corner(aabb, c, p);
260
261 backtransform(piece, scale, p, o);
262
263 // transform to roi_in space, get aabb.
264 adjust_aabb(o, aabb_in);
265 }
266
268 const float IW = (float)interpolation->width * scale;
269
270 const float orig_w = roi_in->scale * piece->buf_in.width, orig_h = roi_in->scale * piece->buf_in.height;
271
272 // adjust roi_in to minimally needed region
273 roi_in->x = fmaxf(0.0f, aabb_in[0] - IW);
274 roi_in->y = fmaxf(0.0f, aabb_in[1] - IW);
275 roi_in->width = fminf(orig_w - roi_in->x, aabb_in[2] - roi_in->x + IW);
276 roi_in->height = fminf(orig_h - roi_in->y, aabb_in[3] - roi_in->y + IW);
277
278 // sanity check.
279 roi_in->x = CLAMP(roi_in->x, 0, (int)floorf(orig_w));
280 roi_in->y = CLAMP(roi_in->y, 0, (int)floorf(orig_h));
281 roi_in->width = CLAMP(roi_in->width, 1, (int)ceilf(orig_w) - roi_in->x);
282 roi_in->height = CLAMP(roi_in->height, 1, (int)ceilf(orig_h) - roi_in->y);
283}
284
285// 3rd (final) pass: you get this input region (may be different from what was requested above),
286// do your best to fill the output region!
289 const void *const ivoid, void *const ovoid)
290{
291 (void)pipe;
292 const dt_iop_roi_t *const roi_in = &piece->roi_in;
293 const dt_iop_roi_t *const roi_out = &piece->roi_out;
294 const int ch = piece->dsc_in.channels;
295 const int ch_width = ch * roi_in->width;
296
297 const float scale = roi_in->scale;
298
301 // (slow) point-by-point transformation.
302 // TODO: optimize with scanlines and linear steps between?
303 for(int j = 0; j < roi_out->height; j++)
304 {
305 float *out = ((float *)ovoid) + (size_t)ch * j * roi_out->width;
306 for(int i = 0; i < roi_out->width; i++, out += ch)
307 {
308 float pi[2], po[2];
309
310 pi[0] = roi_out->x + i;
311 pi[1] = roi_out->y + j;
312
313 backtransform(piece, scale, pi, po);
314
315 po[0] -= roi_in->x;
316 po[1] -= roi_in->y;
317
318 dt_interpolation_compute_pixel4c(interpolation, (float *)ivoid, out, po[0], po[1], roi_in->width,
319 roi_in->height, ch_width);
320 }
321 }
322 return 0;
323}
324
327{
330
331 d->rx = p->rx;
332 d->ry = p->ry;
333
334 const float angle = p->angle * M_PI / 180.0f;
335
336 float rt[] = { cosf(angle), sinf(angle), -sinf(angle), cosf(angle) };
337 for(int k = 0; k < 4; k++) d->m[k] = rt[k];
338
339 // this should not be used for normal images
340 // (i.e. for those, when this iop is off by default)
341 if((d->rx == 0u) && (d->ry == 0u)) piece->enabled = 0;
342}
343
349
351{
352 dt_free_align(piece->data);
353 piece->data = NULL;
354}
355
357{
359
360 const dt_image_t *const image = &(self->dev->image_storage);
361
362 *d = (dt_iop_rotatepixels_params_t){ .rx = 0u, .ry = image->fuji_rotation_pos, .angle = -45.0f };
363
364 self->default_enabled = ((d->rx != 0u) || (d->ry != 0u));
365
366 // FIXME: does not work.
367 self->hide_enable_button = !self->default_enabled;
368
369 if(self->widget)
370 gtk_label_set_text(GTK_LABEL(self->widget), self->default_enabled
371 ? _("automatic pixel rotation")
372 : _("automatic pixel rotation\nonly works for the sensors that need it."));
373}
374
376{
377}
379{
380 IOP_GUI_ALLOC(rotatepixels);
381
382 self->widget = dt_ui_label_new("");
383 gtk_label_set_line_wrap(GTK_LABEL(self->widget), TRUE);
384
385}
386
387// clang-format off
388// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
389// vim: shiftwidth=2 expandtab tabstop=2 cindent
390// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
391// clang-format on
#define TRUE
Definition ashift_lsd.c:162
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
@ IOP_CS_RGB
const dt_colormatrix_t dt_aligned_pixel_t out
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
#define dt_free_align(ptr)
Definition darktable.h:481
static void * dt_calloc_align(size_t size)
Definition darktable.h:488
float dt_boundingbox_t[4]
Definition darktable.h:709
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define __OMP_DECLARE_SIMD__(...)
Definition darktable.h:263
#define __DT_CLONE_TARGETS__
Definition darktable.h:367
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:258
#define __OMP_PARALLEL_FOR_SIMD__(...)
Definition darktable.h:259
void dt_iop_params_t
Definition dev_history.h:41
static GtkWidget * dt_ui_label_new(const gchar *str)
Definition gtk.h:461
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
Definition imageop.c:3141
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:169
@ IOP_FLAGS_UNSAFE_COPY
Definition imageop.h:177
@ IOP_FLAGS_ONE_INSTANCE
Definition imageop.h:172
@ IOP_FLAGS_TILING_FULL_ROI
Definition imageop.h:171
@ IOP_GROUP_TECHNICAL
Definition imageop.h:143
#define IOP_GUI_ALLOC(module)
Definition imageop.h:599
@ IOP_TAG_DISTORT
Definition imageop.h:151
void *const ovoid
const struct dt_interpolation * dt_interpolation_new(enum dt_interpolation_type type)
__DT_CLONE_TARGETS__ void dt_interpolation_compute_pixel4c(const struct dt_interpolation *itor, const float *in, float *out, const float x, const float y, const int width, const int height, const int linestride)
@ DT_INTERPOLATION_USERPREF
static const float x
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
static void mul_mat_vec_2(const float *m, const float *p, float *o)
Definition math.h:176
#define M_PI
Definition math.h:45
int operation_tags()
void distort_mask(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece, const float *const in, float *const out, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
const char ** description(struct dt_iop_module_t *self)
void modify_roi_out(dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_roi_t *roi_out, const dt_iop_roi_t *const roi_in)
int default_group()
int distort_backtransform(dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, float *const restrict points, size_t points_count)
int distort_transform(dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, float *const restrict points, size_t points_count)
void gui_update(dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
static void get_corner(const float *aabb, const int i, float *p)
__DT_CLONE_TARGETS__ int process(dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid)
void modify_roi_in(dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t *const roi_out, dt_iop_roi_t *roi_in)
const char * name()
void gui_init(dt_iop_module_t *self)
void commit_params(dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
void reload_defaults(dt_iop_module_t *self)
static void transform(const dt_dev_pixelpipe_iop_t *const piece, const float scale, const float *const x, float *o)
void cleanup_pipe(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
dt_iop_rotatepixels_gui_data_t dummy
int flags()
static void adjust_aabb(const float *p, float *aabb)
void init_pipe(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static void backtransform(const dt_dev_pixelpipe_iop_t *const piece, const float scale, const float *const x, float *o)
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
dt_image_t image_storage
Definition develop.h:259
uint32_t fuji_rotation_pos
Definition image.h:355
unsigned int channels
Definition format.h:54
int32_t hide_enable_button
Definition imageop.h:262
dt_iop_params_t * default_params
Definition imageop.h:307
GtkWidget * widget
Definition imageop.h:337
struct dt_develop_t * dev
Definition imageop.h:296
gboolean default_enabled
Definition imageop.h:303
Region of interest passed through the pixelpipe.
Definition imageop.h:72
double scale
Definition imageop.h:74
#define MAX(a, b)
Definition thinplate.c:29