Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
borders.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2011 Brian Teague.
4 Copyright (C) 2011-2012 Henrik Andersson.
5 Copyright (C) 2011-2013, 2016 johannes hanika.
6 Copyright (C) 2011 Olivier Tribout.
7 Copyright (C) 2011, 2013 Pascal de Bruijn.
8 Copyright (C) 2011 Robert Bieber.
9 Copyright (C) 2011 Rostyslav Pidgornyi.
10 Copyright (C) 2011-2014, 2016, 2018-2019 Tobias Ellinghaus.
11 Copyright (C) 2012 Loic Guibert.
12 Copyright (C) 2012 parafin.
13 Copyright (C) 2012 Richard Levitte.
14 Copyright (C) 2012 Richard Wonka.
15 Copyright (C) 2012-2016 Ulrich Pegelow.
16 Copyright (C) 2013, 2020-2021 Aldric Renaudin.
17 Copyright (C) 2013 Dennis Gnad.
18 Copyright (C) 2013, 2017-2022 Pascal Obry.
19 Copyright (C) 2013-2016 Roman Lebedev.
20 Copyright (C) 2015 Pedro Côrte-Real.
21 Copyright (C) 2018-2021, 2023-2026 Aurélien PIERRE.
22 Copyright (C) 2018-2019 Edgardo Hoszowski.
23 Copyright (C) 2018 Maurizio Paglia.
24 Copyright (C) 2018 rawfiner.
25 Copyright (C) 2019 Barna Keresztes.
26 Copyright (C) 2019-2022 Diederik Ter Rahe.
27 Copyright (C) 2019 Diederik ter Rahe.
28 Copyright (C) 2019 Jacques Le Clerc.
29 Copyright (C) 2020 Chris Elston.
30 Copyright (C) 2020 Hubert Kowalski.
31 Copyright (C) 2020-2021 Ralf Brown.
32 Copyright (C) 2021 luzpaz.
33 Copyright (C) 2022 Hanno Schwalm.
34 Copyright (C) 2022 Martin Bařinka.
35 Copyright (C) 2022 Philipp Lutz.
36
37 darktable is free software: you can redistribute it and/or modify
38 it under the terms of the GNU General Public License as published by
39 the Free Software Foundation, either version 3 of the License, or
40 (at your option) any later version.
41
42 darktable is distributed in the hope that it will be useful,
43 but WITHOUT ANY WARRANTY; without even the implied warranty of
44 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
45 GNU General Public License for more details.
46
47 You should have received a copy of the GNU General Public License
48 along with darktable. If not, see <http://www.gnu.org/licenses/>.
49*/
50#ifdef HAVE_CONFIG_H
51#include "config.h"
52#endif
53#include "widgets/bauhaus.h"
54#include "system/openmp.h"
56#include "system/mem_alloc.h"
57#include "system/simd.h"
58#include "common/logging.h"
60#include "common/imagebuf.h"
61#include "common/opencl.h"
62#include "develop/develop.h"
64#include "develop/imageop.h"
65#include "develop/imageop_gui.h"
66#include "develop/imageop_gui.h"
67
69#include "gui/presets.h"
70#include "iop/iop_api.h"
71
72#include <gdk/gdkkeysyms.h>
73#include <gtk/gtk.h>
74#include <inttypes.h>
75#include <math.h>
76#include <stdlib.h>
77#include <string.h>
78
80
81// Module constants
82#define DT_IOP_BORDERS_ASPECT_COUNT 12
83#define DT_IOP_BORDERS_ASPECT_IMAGE_IDX 0
84#define DT_IOP_BORDERS_ASPECT_CONSTANT_IDX 11
85#define DT_IOP_BORDERS_ASPECT_IMAGE_VALUE 0.0f
86#define DT_IOP_BORDERS_ASPECT_CONSTANT_VALUE -1.0f
87#define DT_IOP_BORDERS_ASPECT_ORIENTATION_AUTO 0
88#define DT_IOP_BORDERS_ASPECT_ORIENTATION_PORTRAIT 1
89#define DT_IOP_BORDERS_ASPECT_ORIENTATION_LANDSCAPE 2
90#define DT_IOP_BORDERS_POSITION_H_COUNT 5
91#define DT_IOP_BORDERS_POSITION_V_COUNT 5
92
94{
95 float color[3]; // border color $DEFAULT: 1.0
96 float aspect; /* aspect ratio of the outer frame w/h
97 $MIN: 1.0 $MAX: 3.0 $DEFAULT: DT_IOP_BORDERS_ASPECT_CONSTANT_VALUE $DESCRIPTION: "aspect ratio" */
98 char aspect_text[20]; /* aspect ratio of the outer frame w/h (user string version)
99 DEFAULT: "constant border" */
100 int aspect_orient; /* aspect ratio orientation
101 $DEFAULT: 0 $DESCRIPTION: "orientation" */
102 float size; /* border width relative to overall frame width
103 $MIN: 0.0 $MAX: 0.5 $DEFAULT: 0.1 $DESCRIPTION: "border size" */
104 float pos_h; /* picture horizontal position ratio into the final image
105 $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.5 $DESCRIPTION: "horizontal offset" */
106 char pos_h_text[20]; /* picture horizontal position ratio into the final image (user string version)
107 DEFAULT: "1/2" */
108 float pos_v; /* picture vertical position ratio into the final image
109 $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.5 $DESCRIPTION: "vertical offset"*/
110 char pos_v_text[20]; /* picture vertical position ratio into the final image (user string version)
111 DEFAULT: "1/2" */
112 float frame_size; /* frame line width relative to border width
113 $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "frame line size" */
114 float frame_offset; /* frame offset from picture size relative to [border width - frame width]
115 $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.5 $DESCRIPTION: "frame line offset" */
116 float frame_color[3]; // frame line color $DEFAULT: 0.0
117 gboolean max_border_size; /* the way border size is computed
118 $DEFAULT: TRUE */
120
141
142// ******* Check and update legacy params...(esp. ver 4)
143int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
144 void *new_params, const int new_version)
145{
146 if(old_version == 1 && new_version == 3)
147 {
148 typedef struct dt_iop_borders_params_v1_t
149 {
150 float color[3]; // border color
151 float aspect; // aspect ratio of the outer frame w/h
152 float size; // border width relative to overall frame width
153 } dt_iop_borders_params_v1_t;
154
155 dt_iop_borders_params_v1_t *o = (dt_iop_borders_params_v1_t *)old_params;
158
159 *n = *d; // start with a fresh copy of default parameters
160 memcpy(n->color, o->color, sizeof(o->color));
161 n->aspect = (o->aspect < 1) ? 1 / o->aspect : o->aspect;
162 // no auto orientation in legacy param due to already convert aspect ratio
163 n->aspect_orient = o->aspect > 1 ? DT_IOP_BORDERS_ASPECT_ORIENTATION_LANDSCAPE
165 n->size = fabsf(o->size); // no negative size any more (was for "constant border" detect)
166 n->max_border_size = FALSE;
167 return 0;
168 }
169
170 if(old_version == 2 && new_version == 3)
171 {
172 typedef struct dt_iop_borders_params_v2_t
173 {
174 float color[3]; // border color
175 float aspect; // aspect ratio of the outer frame w/h
176 char aspect_text[20]; // aspect ratio of the outer frame w/h (user string version)
177 int aspect_orient; // aspect ratio orientation
178 float size; // border width relative to overall frame width
179 float pos_h; // picture horizontal position ratio into the final image
180 char pos_h_text[20]; // picture horizontal position ratio into the final image (user string version)
181 float pos_v; // picture vertical position ratio into the final image
182 char pos_v_text[20]; // picture vertical position ratio into the final image (user string version)
183 float frame_size; // frame line width relative to border width
184 float frame_offset; // frame offset from picture size relative to [border width - frame width]
185 float frame_color[3]; // frame line color
186 } dt_iop_borders_params_v2_t;
187
188 dt_iop_borders_params_v2_t *o = (dt_iop_borders_params_v2_t *)old_params;
190
191 memcpy(n, o, sizeof(struct dt_iop_borders_params_v2_t));
192 n->max_border_size = FALSE;
193 return 0;
194 }
195
196 return 1;
197}
198
203
204
206
207
208const char *name()
209{
210 return _("framing");
211}
212
213const char **description(struct dt_iop_module_t *self)
214{
215 return dt_iop_set_description(self, _("add solid borders or margins around the picture"),
216 _("creative"),
217 _("linear or non-linear, RGB, display-referred"),
218 _("geometric, RGB"),
219 _("linear or non-linear, RGB, display-referred"));
220}
221
222
224{
225 return IOP_GROUP_EFFECTS;
226}
227
232
237
239{
240 return IOP_CS_RGB;
241}
242
243/* --- the shared geometry core ----------------------------------------------------------
244 *
245 * The border's offset is DERIVED, not a parameter: it is the difference between the output and
246 * input sizes, apportioned by pos_h/pos_v. Both the pixel pipe's distort callbacks and the
247 * geometry service's record (develop/geometry/geometry.h) go through the two helpers below, so
248 * the size map and the offset can never drift apart.
249 */
250
252static void _borders_offset(const dt_iop_borders_data_t *const d, const dt_iop_roi_t *const in,
253 const dt_iop_roi_t *const out, int *left, int *top)
254{
255 *left = (out->width - in->width) * d->pos_h;
256 *top = (out->height - in->height) * d->pos_v;
257}
258
260 float *const restrict points, size_t points_count)
261{
263
264 int border_size_l = 0, border_size_t = 0;
265 _borders_offset(d, &piece->buf_in, &piece->buf_out, &border_size_l, &border_size_t);
266
267 // nothing to be done if parameters are set to neutral values (no top/left border)
268 if (border_size_l == 0 && border_size_t == 0) return 1;
269 __OMP_PARALLEL_FOR_SIMD__(if(points_count > 100) aligned(points:64))
270 for(size_t i = 0; i < points_count * 2; i += 2)
271 {
272 points[i] += border_size_l;
273 points[i + 1] += border_size_t;
274 }
275
276 return 1;
277}
279 float *const restrict points, size_t points_count)
280{
282
283 int border_size_l = 0, border_size_t = 0;
284 _borders_offset(d, &piece->buf_in, &piece->buf_out, &border_size_l, &border_size_t);
285
286 // nothing to be done if parameters are set to neutral values (no top/left border)
287 if (border_size_l == 0 && border_size_t == 0) return 1;
288 __OMP_PARALLEL_FOR_SIMD__(if(points_count > 100) aligned(points:64))
289 for(size_t i = 0; i < points_count * 2; i += 2)
290 {
291 points[i] -= border_size_l;
292 points[i + 1] -= border_size_t;
293 }
294
295 return 1;
296}
297
298void distort_mask(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece,
299 const float *const in, float *const out, const dt_iop_roi_t *const roi_in,
300 const dt_iop_roi_t *const roi_out)
301{
302 (void)pipe;
304
305 const int border_tot_width = (piece->buf_out.width - piece->buf_in.width) * roi_in->scale;
306 const int border_tot_height = (piece->buf_out.height - piece->buf_in.height) * roi_in->scale;
307 const int border_size_t = border_tot_height * d->pos_v;
308 const int border_size_l = border_tot_width * d->pos_h;
309 const int border_in_x = MAX(border_size_l - roi_out->x, 0);
310 const int border_in_y = MAX(border_size_t - roi_out->y, 0);
311
312 // fill the image with 0 so that the added border isn't part of the mask
313 dt_iop_image_fill(out, 0.0f, roi_out->width, roi_out->height, 1);
314
315 // blit image inside border and fill the output with previous processed out
317 for(int j = 0; j < roi_in->height; j++)
318 {
319 float *outb = out + (size_t)(j + border_in_y) * roi_out->width + border_in_x;
320 const float *inb = in + (size_t)j * roi_in->width;
321 memcpy(outb, inb, sizeof(float) * roi_in->width);
322 }
323}
324
325// 1st pass: how large would the output be, given this input roi?
326// this is always called with the full buffer before processing.
330static void _borders_map_size(const dt_iop_borders_data_t *const d, const dt_iop_roi_t *const roi_in,
331 dt_iop_roi_t *roi_out)
332{
333 *roi_out = *roi_in;
334 const float size = fabsf(d->size);
335 if(size == 0.0f || size >= 1.0f) return;
336
338 {
339 // base computation on the larger border to keep constant size regardless of orientation
340 if(roi_in->width > roi_in->height || !d->max_border_size)
341 {
342 // relative to width, constant for height as well
343 roi_out->width = (float)roi_in->width / (1.0f - size);
344 roi_out->height = roi_in->height + roi_out->width - roi_in->width;
345 }
346 else
347 {
348 // relative to height, constant for width as well
349 roi_out->height = (float)roi_in->height / (1.0f - size);
350 roi_out->width = roi_in->width + roi_out->height - roi_in->height;
351 }
352 }
353 else
354 {
355 const float image_aspect = roi_in->width / (float)roi_in->height;
356 float aspect = (d->aspect == DT_IOP_BORDERS_ASPECT_IMAGE_VALUE) ? image_aspect : d->aspect;
357 if(aspect <= 0.0f) return;
358
359 if(d->aspect_orient == DT_IOP_BORDERS_ASPECT_ORIENTATION_AUTO)
360 aspect = ((image_aspect < 1.0f) != (aspect < 1.0f)) ? 1.0f / aspect : aspect;
361 else if(d->aspect_orient == DT_IOP_BORDERS_ASPECT_ORIENTATION_LANDSCAPE)
362 aspect = (aspect < 1.0f) ? 1.0f / aspect : aspect;
363 else if(d->aspect_orient == DT_IOP_BORDERS_ASPECT_ORIENTATION_PORTRAIT)
364 aspect = (aspect > 1.0f) ? 1.0f / aspect : aspect;
365
366 const float inv_size = 1.0f / (1.0f - size);
367 roi_out->width = (float)roi_in->width * inv_size;
368 roi_out->height = (float)roi_out->width / aspect;
369
370 // if height constraint is tighter, use it as the driver axis instead
371 const float min_height = (float)roi_in->height * inv_size;
372 if(roi_out->height < min_height)
373 {
374 roi_out->height = min_height;
375 roi_out->width = roi_out->height * aspect;
376 }
377 }
378}
379
380void modify_roi_out(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe,
381 struct dt_dev_pixelpipe_iop_t *piece, dt_iop_roi_t *roi_out,
382 const dt_iop_roi_t *roi_in)
383{
384 _borders_map_size((const dt_iop_borders_data_t *)piece->data, roi_in, roi_out);
385}
386
387// 2nd pass: which roi would this operation need as input to fill the given output region?
388void modify_roi_in(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe,
389 struct dt_dev_pixelpipe_iop_t *piece,
390 const dt_iop_roi_t *roi_out, dt_iop_roi_t *roi_in)
391{
393 *roi_in = *roi_out;
394 const int bw = (piece->buf_out.width - piece->buf_in.width) * roi_out->scale;
395 const int bh = (piece->buf_out.height - piece->buf_in.height) * roi_out->scale;
396
397 // don't request outside image (no px for borders)
398 roi_in->x = MAX(roi_out->x - bw * d->pos_h, 0);
399 roi_in->y = MAX(roi_out->y - bh * d->pos_v, 0);
400 // subtract upper left border from dimensions
401 roi_in->width -= MAX(bw * d->pos_h - roi_out->x, 0);
402 roi_in->height -= MAX(bh * d->pos_v - roi_out->y, 0);
403
404 // subtract lower right border from dimensions
405 roi_in->width -= roi_out->scale
406 * MAX((roi_in->x + roi_in->width) / roi_out->scale - (piece->buf_in.width), 0);
407 roi_in->height -= roi_out->scale
408 * MAX((roi_in->y + roi_in->height) / roi_out->scale - (piece->buf_in.height), 0);
409 // don't request nothing or outside roi
410 roi_in->width = MIN(roi_out->scale * piece->buf_in.width, MAX(1, roi_in->width));
411 roi_in->height = MIN(roi_out->scale * piece->buf_in.height, MAX(1, roi_in->height));
412 // FIXME: clamping to 1 leads to a one-pixel visual glitch if the right/bottom border completely fills the
413 // FIXME: viewport, but changing it to 0 breaks all of the tiling_callback functions with a division by zero
414}
415
417{
420 int border_top; // 0..bt is rows of top border outside the frameline
421 int fl_top; //bt..ft is the top frameline
422 int image_top; //ft..it is the top border inside the frameline
423 int border_left; // 0..bl is columns of left border outside the frameline
424 int fl_left; //bl..fl is the left frameline
425 int image_left; //fl..il is the left border inside the frameline
426 int image_right; //il..ir is the actual image area
427 int fl_right; //ir..fr is the right border inside the frameline
428 int border_right; //fr..br is the right frameeline
429 int width; //br..width is the right border outside the frameline
430 int image_bot; //it..ib is the actual image area
431 int fl_bot; //ib..fb is the bottom border inside the frameline
432 int border_bot; //fb..bt is the frameline
433 int height; //bt..height is the bottom border outside the frameline
434 int stride; // width of input roi
435};
436
437// this will be called from inside an OpenMP parallel section, so no need to parallelize further
438static inline void set_pixels(float *buf, const dt_aligned_pixel_t color, const int npixels)
439{
440 for (int i = 0; i < npixels; i++)
441 {
442 __OMP_SIMD__(aligned(buf, color : 16))
443 for (int c = 0; c < 4; c++)
444 {
445 buf[4*i+c] = color[c];
446 }
447 }
448}
449
450// this will be called from inside an OpenMP parallel section, so no need to parallelize further
451static inline void copy_pixels(float *out, const float *const in, const int npixels)
452{
453 for (int i = 0; i < npixels; i++)
454 {
455 __OMP_SIMD__(aligned(in, out : 16))
456 for (int c = 0; c < 4; c++)
457 {
458 out[4*i+c] = in[4*i+c];
459 }
460 }
461}
462
464void copy_image_with_border(float *out, const float *const in, const struct border_positions_t *binfo)
465{
466 const int image_width = binfo->image_right - binfo->image_left;
468 for (size_t row = 0; row < binfo->height; row++)
469 {
470 float *outrow = out + 4 * row * binfo->width;
471 if (row < binfo->border_top || row >= binfo->border_bot)
472 {
473 // top/bottom border outside the frameline: entirely the border color
474 set_pixels(outrow, binfo->bcolor, binfo->width);
475 }
476 else if (row < binfo->fl_top || row >= binfo->fl_bot)
477 {
478 // top/bottom frameline
479 set_pixels(outrow, binfo->bcolor, binfo->border_left);
480 set_pixels(outrow + 4*binfo->border_left, binfo->flcolor, binfo->border_right - binfo->border_left);
481 set_pixels(outrow + 4*binfo->border_right, binfo->bcolor, binfo->width - binfo->border_right);
482 }
483 else if (row < binfo->image_top || row >= binfo->image_bot)
484 {
485 // top/bottom border inside the frameline
486 set_pixels(outrow, binfo->bcolor, binfo->border_left);
487 set_pixels(outrow + 4*binfo->border_left, binfo->flcolor, binfo->fl_left - binfo->border_left);
488 set_pixels(outrow + 4*binfo->fl_left, binfo->bcolor, binfo->fl_right - binfo->fl_left);
489 set_pixels(outrow + 4*binfo->fl_right, binfo->flcolor, binfo->border_right - binfo->fl_right);
490 set_pixels(outrow + 4*binfo->border_right, binfo->bcolor, binfo->width - binfo->border_right);
491 }
492 else
493 {
494 // image area: set left border (w/optional frame line), copy image row, set right border (w/optional frame line)
495 // set outer border
496 set_pixels(outrow, binfo->bcolor, binfo->border_left);
497 if (binfo->image_left > binfo->border_left)
498 {
499 // we have a frameline, so set it and the inner border
500 set_pixels(outrow + 4*binfo->border_left, binfo->flcolor, binfo->fl_left - binfo->border_left);
501 set_pixels(outrow + 4*binfo->fl_left, binfo->bcolor, binfo->image_left - binfo->fl_left);
502 }
503 // copy image row
504 copy_pixels(outrow + 4*binfo->image_left, in + 4 * (row - binfo->image_top) * binfo->stride, image_width);
505 // set right border
506 set_pixels(outrow + 4*binfo->image_right, binfo->bcolor, binfo->fl_right - binfo->image_right);
507 if (binfo->width > binfo->fl_right)
508 {
509 // we have a frameline, so set it and the outer border
510 set_pixels(outrow + 4*binfo->fl_right, binfo->flcolor, binfo->border_right - binfo->fl_right);
511 set_pixels(outrow + 4*binfo->border_right, binfo->bcolor, binfo->width - binfo->border_right);
512 }
513 }
514 }
515
516}
517
518int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
519 void *const ovoid)
520{
521 const dt_iop_roi_t *const roi_in = &piece->roi_in;
522 const dt_iop_roi_t *const roi_out = &piece->roi_out;
523 const dt_iop_borders_data_t *const d = (dt_iop_borders_data_t *)piece->data;
524
525 const int border_tot_width = (piece->buf_out.width - piece->buf_in.width) * roi_in->scale;
526 const int border_tot_height = (piece->buf_out.height - piece->buf_in.height) * roi_in->scale;
527 const int border_size_t = border_tot_height * d->pos_v;
528 const int border_size_b = border_tot_height - border_size_t;
529 const int border_size_l = border_tot_width * d->pos_h;
530 const int border_size_r = border_tot_width - border_size_l;
531 const int border_in_x = MAX(border_size_l - roi_out->x, 0);
532 const int border_in_y = MAX(border_size_t - roi_out->y, 0);
533
534 // compute frame line parameters
535 const int border_min_size = MIN(MIN(border_size_t, border_size_b), MIN(border_size_l, border_size_r));
536 const int frame_size = border_min_size * d->frame_size;
537
538 const int b_in_x = CLAMP(border_in_x, 0, roi_out->width - 1);
539 struct border_positions_t binfo =
540 { .bcolor = { d->color[0], d->color[1], d->color[2], 1.0f },
541 .flcolor = { d->frame_color[0], d->frame_color[1], d->frame_color[2], 1.0f },
542 .border_top = border_in_y,
543 .fl_top = border_in_y,
544 .image_top = border_in_y,
545 .border_left = b_in_x,
546 .fl_left = b_in_x,
547 .image_left = b_in_x,
548 .image_right = b_in_x + roi_in->width,
549 .fl_right = roi_out->width,
550 .border_right = roi_out->width,
551 .width = roi_out->width,
552 .image_bot = border_in_y + roi_in->height,
553 .fl_bot = roi_out->height,
554 .border_bot = roi_out->height,
555 .height = roi_out->height,
556 .stride = roi_in->width
557 };
558 if (frame_size > 0)
559 {
560 const int image_lx = border_size_l - roi_out->x;
561 const int image_ty = border_size_t - roi_out->y;
562 const int frame_space = border_min_size - frame_size;
563 const int frame_offset = frame_space * d->frame_offset;
564 const int frame_tl_in_x = MAX(border_in_x - frame_offset, 0);
565 const int frame_tl_out_x = MAX(frame_tl_in_x - frame_size, 0);
566 const int frame_tl_in_y = MAX(border_in_y - frame_offset, 0);
567 const int frame_tl_out_y = MAX(frame_tl_in_y - frame_size, 0);
568 binfo.border_top = frame_tl_out_y;
569 binfo.fl_top = frame_tl_in_y;
570 binfo.border_left = CLAMP(frame_tl_out_x, 0, roi_out->width);
571 binfo.fl_left = CLAMP(frame_tl_in_x, 0, roi_out->width);
572 const int frame_in_width = floor((piece->buf_in.width * roi_in->scale) + frame_offset * 2);
573 const int frame_in_height = floor((piece->buf_in.height * roi_in->scale) + frame_offset * 2);
574 const int frame_out_width = frame_in_width + frame_size * 2;
575 const int frame_out_height = frame_in_height + frame_size * 2;
576 const int frame_br_in_x = CLAMP(image_lx - frame_offset + frame_in_width - 1, 0, roi_out->width - 1);
577 const int frame_br_in_y = CLAMP(image_ty - frame_offset + frame_in_height - 1, 0, roi_out->height - 1);
578 // ... if 100% frame_offset we ensure frame_line "stick" the out border
579 const int frame_br_out_x
580 = (d->frame_offset == 1.0f && (border_min_size == MIN(border_size_l, border_size_r)))
581 ? (roi_out->width)
582 : CLAMP(image_lx - frame_offset - frame_size + frame_out_width - 1, 0, roi_out->width - 1);
583 const int frame_br_out_y
584 = (d->frame_offset == 1.0f && (border_min_size == MIN(border_size_t, border_size_b)))
585 ? (roi_out->height)
586 : CLAMP(image_ty - frame_offset - frame_size + frame_out_height - 1, 0, roi_out->height - 1);
587 binfo.fl_right = frame_br_in_x + 1; // need end+1 for these coordinates
588 binfo.border_right = frame_br_out_x + 1;
589 binfo.fl_bot = frame_br_in_y + 1;
590 binfo.border_bot = frame_br_out_y + 1;
591 }
592 copy_image_with_border((float*)ovoid, (const float*)ivoid, &binfo);
593 return 0;
594}
595
596#ifdef HAVE_OPENCL
597int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
598{
599 const dt_iop_roi_t *const roi_in = &piece->roi_in;
600 const dt_iop_roi_t *const roi_out = &piece->roi_out;
603
604 cl_int err = -999;
605 const int devid = pipe->devid;
606
607 const int width = roi_out->width;
608 const int height = roi_out->height;
609
610 const int border_tot_width = (piece->buf_out.width - piece->buf_in.width) * roi_in->scale;
611 const int border_tot_height = (piece->buf_out.height - piece->buf_in.height) * roi_in->scale;
612 const int border_size_t = border_tot_height * d->pos_v;
613 const int border_size_b = border_tot_height - border_size_t;
614 const int border_size_l = border_tot_width * d->pos_h;
615 const int border_size_r = border_tot_width - border_size_l;
616 const int border_in_x = MAX(border_size_l - roi_out->x, 0);
617 const int border_in_y = MAX(border_size_t - roi_out->y, 0);
618
619 // ----- Filling border
620 const float col[4] = { d->color[0], d->color[1], d->color[2], 1.0f };
621 size_t sizes[2] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid) };
622 const int zero = 0;
623 dt_opencl_set_kernel_arg(devid, gd->kernel_borders_fill, 0, sizeof(cl_mem), &dev_out);
624 dt_opencl_set_kernel_arg(devid, gd->kernel_borders_fill, 1, sizeof(int), &zero);
625 dt_opencl_set_kernel_arg(devid, gd->kernel_borders_fill, 2, sizeof(int), &zero);
626 dt_opencl_set_kernel_arg(devid, gd->kernel_borders_fill, 3, sizeof(int), &width);
627 dt_opencl_set_kernel_arg(devid, gd->kernel_borders_fill, 4, sizeof(int), &height);
628 dt_opencl_set_kernel_arg(devid, gd->kernel_borders_fill, 5, 4 * sizeof(float), &col);
629 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_borders_fill, sizes);
630 if(err != CL_SUCCESS) goto error;
631
632 // ----- Frame line
633 const int border_min_size = MIN(MIN(border_size_t, border_size_b), MIN(border_size_l, border_size_r));
634 const int frame_size = border_min_size * d->frame_size;
635 if(frame_size != 0)
636 {
637 const float col_frame[4] = { d->frame_color[0], d->frame_color[1], d->frame_color[2], 1.0f };
638 const int image_lx = border_size_l - roi_out->x;
639 const int image_ty = border_size_t - roi_out->y;
640 const int frame_space = border_min_size - frame_size;
641 const int frame_offset = frame_space * d->frame_offset;
642 const int frame_tl_in_x = MAX(border_in_x - frame_offset, 0);
643 const int frame_tl_out_x = MAX(frame_tl_in_x - frame_size, 0);
644 const int frame_tl_in_y = MAX(border_in_y - frame_offset, 0);
645 const int frame_tl_out_y = MAX(frame_tl_in_y - frame_size, 0);
646 const int frame_in_width = floor((piece->buf_in.width * roi_in->scale) + frame_offset * 2);
647 const int frame_in_height = floor((piece->buf_in.height * roi_in->scale) + frame_offset * 2);
648 const int frame_out_width = frame_in_width + frame_size * 2;
649 const int frame_out_height = frame_in_height + frame_size * 2;
650 const int frame_br_in_x = CLAMP(image_lx - frame_offset + frame_in_width - 1, 0, roi_out->width - 1);
651 const int frame_br_in_y = CLAMP(image_ty - frame_offset + frame_in_height - 1, 0, roi_out->height - 1);
652 // ... if 100% frame_offset we ensure frame_line "stick" the out border
653 const int frame_br_out_x
654 = (d->frame_offset == 1.0f && (border_min_size == MIN(border_size_l, border_size_r)))
655 ? (roi_out->width)
656 : CLAMP(image_lx - frame_offset - frame_size + frame_out_width - 1, 0, roi_out->width);
657 const int frame_br_out_y
658 = (d->frame_offset == 1.0f && (border_min_size == MIN(border_size_t, border_size_b)))
659 ? (roi_out->height)
660 : CLAMP(image_ty - frame_offset - frame_size + frame_out_height - 1, 0, roi_out->height);
661
662 const int roi_frame_in_width = frame_br_in_x - frame_tl_in_x;
663 const int roi_frame_in_height = frame_br_in_y - frame_tl_in_y;
664 const int roi_frame_out_width = frame_br_out_x - frame_tl_out_x;
665 const int roi_frame_out_height = frame_br_out_y - frame_tl_out_y;
666
667 dt_opencl_set_kernel_arg(devid, gd->kernel_borders_fill, 0, sizeof(cl_mem), &dev_out);
668 dt_opencl_set_kernel_arg(devid, gd->kernel_borders_fill, 1, sizeof(int), &frame_tl_out_x);
669 dt_opencl_set_kernel_arg(devid, gd->kernel_borders_fill, 2, sizeof(int), &frame_tl_out_y);
670 dt_opencl_set_kernel_arg(devid, gd->kernel_borders_fill, 3, sizeof(int), &roi_frame_out_width);
671 dt_opencl_set_kernel_arg(devid, gd->kernel_borders_fill, 4, sizeof(int), &roi_frame_out_height);
672 dt_opencl_set_kernel_arg(devid, gd->kernel_borders_fill, 5, 4 * sizeof(float), &col_frame);
673 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_borders_fill, sizes);
674 if(err != CL_SUCCESS) goto error;
675
676 dt_opencl_set_kernel_arg(devid, gd->kernel_borders_fill, 0, sizeof(cl_mem), &dev_out);
677 dt_opencl_set_kernel_arg(devid, gd->kernel_borders_fill, 1, sizeof(int), &frame_tl_in_x);
678 dt_opencl_set_kernel_arg(devid, gd->kernel_borders_fill, 2, sizeof(int), &frame_tl_in_y);
679 dt_opencl_set_kernel_arg(devid, gd->kernel_borders_fill, 3, sizeof(int), &roi_frame_in_width);
680 dt_opencl_set_kernel_arg(devid, gd->kernel_borders_fill, 4, sizeof(int), &roi_frame_in_height);
681 dt_opencl_set_kernel_arg(devid, gd->kernel_borders_fill, 5, 4 * sizeof(float), &col);
682 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_borders_fill, sizes);
683 if(err != CL_SUCCESS) goto error;
684 }
685
686 size_t iorigin[] = { 0, 0, 0 };
687 size_t oorigin[] = { border_in_x, border_in_y, 0 };
688 size_t region[] = { roi_in->width, roi_in->height, 1 };
689
690 // copy original input from dev_in -> dev_out as starting point
691 err = dt_opencl_enqueue_copy_image(devid, dev_in, dev_out, iorigin, oorigin, region);
692 if(err != CL_SUCCESS) goto error;
693
694 return TRUE;
695
696error:
697 dt_print(DT_DEBUG_OPENCL, "[opencl_borders] couldn't enqueue kernel! %d\n", err);
698 return FALSE;
699}
700#endif
701
702
704{
705 const int program = 2; // basic.cl from programs.conf
708 module->data = gd;
709 gd->kernel_borders_fill = dt_opencl_create_kernel(program, "borders_fill");
710}
711
712
719
720
721/* --- the geometry service's view of this module (develop/geometry/geometry.h) --------- */
722
723static void _borders_geometry_map_size(const void *data, const dt_iop_roi_t *const in, dt_iop_roi_t *out)
724{
725 _borders_map_size((const dt_iop_borders_data_t *)data, in, out);
726}
727
728static int _borders_geometry_transform(const void *data, const dt_geometry_record_t *const record,
729 dt_geometry_chain_t *chain, float *points, size_t points_count)
730{
731 int left = 0, top = 0;
732 _borders_offset((const dt_iop_borders_data_t *)data, &record->in, &record->out, &left, &top);
733 if(left == 0 && top == 0) return 1;
734 for(size_t i = 0; i < points_count * 2; i += 2)
735 {
736 points[i] += left;
737 points[i + 1] += top;
738 }
739 return 1;
740}
741
742static int _borders_geometry_backtransform(const void *data, const dt_geometry_record_t *const record,
743 dt_geometry_chain_t *chain, float *points, size_t points_count)
744{
745 int left = 0, top = 0;
746 _borders_offset((const dt_iop_borders_data_t *)data, &record->in, &record->out, &left, &top);
747 if(left == 0 && top == 0) return 1;
748 for(size_t i = 0; i < points_count * 2; i += 2)
749 {
750 points[i] -= left;
751 points[i + 1] -= top;
752 }
753 return 1;
754}
755
761
762gboolean geometry_record(dt_iop_module_t *self, const void *params, dt_geometry_record_t *record)
763{
764 /* commit_params() is a straight copy of the parameters into the data struct -- the two are the
765 * same type -- so the record carries that copy and nothing is derived twice. */
767 if(IS_NULL_PTR(data)) return FALSE;
768 memcpy(data, params, sizeof(dt_iop_borders_params_t));
769
770 record->data = data;
771 record->free_data = dt_free_gpointer;
773 return TRUE;
774}
775
783
785{
787 piece->data_size = sizeof(dt_iop_borders_data_t);
788}
789
791{
792 dt_free_align(piece->data);
793 piece->data = NULL;
794}
795
797{
798 dt_iop_borders_params_t p = (dt_iop_borders_params_t){ { 1.0f, 1.0f, 1.0f },
799 3.0f / 2.0f,
800 "3:2",
801 0,
802 0.1f,
803 0.5f,
804 "1/2",
805 0.5f,
806 "1/2",
807 0.0f,
808 0.5f,
809 { 0.0f, 0.0f, 0.0f },
810 TRUE };
811 dt_gui_presets_add_generic(_("15:10 postcard white"), self->op,
812 self->version(), &p, sizeof(p), 1);
813
814 p.color[0] = p.color[1] = p.color[2] = 0.0f;
815 p.frame_color[0] = p.frame_color[1] = p.frame_color[2] = 1.0f;
816 dt_gui_presets_add_generic(_("15:10 postcard black"), self->op,
817 self->version(), &p, sizeof(p), 1);
818}
819
821{
824
825 if(fabsf(p->color[0] - self->picked_color[0]) < 0.0001f
826 && fabsf(p->color[1] - self->picked_color[1]) < 0.0001f
827 && fabsf(p->color[2] - self->picked_color[2]) < 0.0001f)
828 {
829 // interrupt infinite loops
830 return;
831 }
832
833 if(fabsf(p->frame_color[0] - self->picked_color[0]) < 0.0001f
834 && fabsf(p->frame_color[1] - self->picked_color[1]) < 0.0001f
835 && fabsf(p->frame_color[2] - self->picked_color[2]) < 0.0001f)
836 {
837 // interrupt infinite loops
838 return;
839 }
840
841 GdkRGBA c = (GdkRGBA){.red = self->picked_color[0],
842 .green = self->picked_color[1],
843 .blue = self->picked_color[2],
844 .alpha = 1.0 };
845
846 if(picker == g->frame_picker)
847 {
848 p->frame_color[0] = self->picked_color[0];
849 p->frame_color[1] = self->picked_color[1];
850 p->frame_color[2] = self->picked_color[2];
851 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(g->frame_colorpick), &c);
852 }
853 else if(picker == g->border_picker)
854 {
855 p->color[0] = self->picked_color[0];
856 p->color[1] = self->picked_color[1];
857 p->color[2] = self->picked_color[2];
858 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(g->colorpick), &c);
859 }
860
861 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
862}
863
864static void aspect_changed(GtkWidget *combo, dt_iop_module_t *self)
865{
868 const int which = dt_bauhaus_combobox_get(combo);
869 const char *text = dt_bauhaus_combobox_get_text(combo);
870 if(which == dt_bauhaus_combobox_length(combo)-1)
871 {
872 g_strlcpy(p->aspect_text, text, sizeof(p->aspect_text));
873 }
874 else if(which < DT_IOP_BORDERS_ASPECT_COUNT)
875 {
876 g_strlcpy(p->aspect_text, text, sizeof(p->aspect_text));
877 p->aspect = g->aspect_ratios[which];
879 dt_bauhaus_slider_set(g->aspect_slider,p->aspect);
881 }
883 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
884}
885
887{
890 const int which = dt_bauhaus_combobox_get(combo);
891 const char *text = dt_bauhaus_combobox_get_text(combo);
892 if(which == dt_bauhaus_combobox_length(combo)-1)
893 {
894 g_strlcpy(p->aspect_text, text, sizeof(p->aspect_text));
895 }
896 else if(which < DT_IOP_BORDERS_POSITION_H_COUNT)
897 {
898 g_strlcpy(p->pos_h_text, text, sizeof(p->pos_h_text));
899 p->pos_h = g->pos_h_ratios[which];
901 dt_bauhaus_slider_set(g->pos_h_slider,p->pos_h);
903 }
905 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
906}
907
909{
912 const int which = dt_bauhaus_combobox_get(combo);
913 const char *text = dt_bauhaus_combobox_get_text(combo);
914 if(which == dt_bauhaus_combobox_length(combo)-1)
915 {
916 g_strlcpy(p->aspect_text, text, sizeof(p->aspect_text));
917 }
918 else if(which < DT_IOP_BORDERS_POSITION_V_COUNT)
919 {
920 g_strlcpy(p->pos_v_text, text, sizeof(p->pos_v_text));
921 p->pos_v = g->pos_v_ratios[which];
923 dt_bauhaus_slider_set(g->pos_v_slider,p->pos_v);
925 }
927 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
928}
929
930void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
931{
933
934 if (w == g->aspect_slider)
935 {
937 }
938 else if(w == g->pos_h_slider)
939 {
941 }
942 else if(w == g->pos_v_slider)
943 {
945 }
946}
947
948static void colorpick_color_set(GtkColorButton *widget, dt_iop_module_t *self)
949{
950 if(dt_gui_widgets_suppressed()) return;
952
953 // turn off the other color picker so that this tool actually works ...
955
956 GdkRGBA c;
957 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &c);
958 p->color[0] = c.red;
959 p->color[1] = c.green;
960 p->color[2] = c.blue;
961
962 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
963}
964
965
966static void frame_colorpick_color_set(GtkColorButton *widget, dt_iop_module_t *self)
967{
968 if(dt_gui_widgets_suppressed()) return;
970
971 // turn off the other color picker so that this tool actually works ...
973
974 GdkRGBA c;
975 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &c);
976 p->frame_color[0] = c.red;
977 p->frame_color[1] = c.green;
978 p->frame_color[2] = c.blue;
979
980 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
981}
982
983void gui_update(struct dt_iop_module_t *self)
984{
987
988// FIXME by hand
989 // ----- Aspect
990 int k = 0;
992 {
993 if(fabsf(p->aspect - g->aspect_ratios[k]) < 0.01f)
994 {
995 dt_bauhaus_combobox_set(g->aspect, k);
996 break;
997 }
998 }
1000 {
1001 dt_bauhaus_combobox_set(g->aspect, k);
1002 }
1003
1004 // ----- Position H
1005 for(k = 0; k < DT_IOP_BORDERS_POSITION_H_COUNT; k++)
1006 {
1007 if(fabsf(p->pos_h - g->pos_h_ratios[k]) < 0.01f)
1008 {
1009 dt_bauhaus_combobox_set(g->pos_h, k);
1010 break;
1011 }
1012 }
1014 {
1015 dt_bauhaus_combobox_set(g->pos_h, k);
1016 }
1017
1018 // ----- Position V
1019 for(k = 0; k < DT_IOP_BORDERS_POSITION_V_COUNT; k++)
1020 {
1021 if(fabsf(p->pos_v - g->pos_v_ratios[k]) < 0.01f)
1022 {
1023 dt_bauhaus_combobox_set(g->pos_v, k);
1024 break;
1025 }
1026 }
1028 {
1029 dt_bauhaus_combobox_set(g->pos_v, k);
1030 }
1031
1032 // ----- Border Color
1033 GdkRGBA c = (GdkRGBA){.red = p->color[0], .green = p->color[1], .blue = p->color[2], .alpha = 1.0 };
1034 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(g->colorpick), &c);
1035
1036 // ----- Frame Color
1037 GdkRGBA fc = (GdkRGBA){
1038 .red = p->frame_color[0], .green = p->frame_color[1], .blue = p->frame_color[2], .alpha = 1.0
1039 };
1040 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(g->frame_colorpick), &fc);
1041}
1042
1043static void gui_init_aspect(struct dt_iop_module_t *self)
1044{
1046
1047 dt_bauhaus_combobox_add(g->aspect, _("image"));
1048 dt_bauhaus_combobox_add(g->aspect, _("3:1"));
1049 dt_bauhaus_combobox_add(g->aspect, _("95:33"));
1050 dt_bauhaus_combobox_add(g->aspect, _("2:1"));
1051 dt_bauhaus_combobox_add(g->aspect, _("16:9"));
1052 dt_bauhaus_combobox_add(g->aspect, _("golden cut"));
1053 dt_bauhaus_combobox_add(g->aspect, _("3:2"));
1054 dt_bauhaus_combobox_add(g->aspect, _("A4"));
1055 dt_bauhaus_combobox_add(g->aspect, _("DIN"));
1056 dt_bauhaus_combobox_add(g->aspect, _("4:3"));
1057 dt_bauhaus_combobox_add(g->aspect, _("square"));
1058 dt_bauhaus_combobox_add(g->aspect, _("constant border"));
1059 dt_bauhaus_combobox_add(g->aspect, _("custom..."));
1060
1063 int i = 1;
1064 g->aspect_ratios[i++] = 3.0f;
1065 g->aspect_ratios[i++] = 95.0f / 33.0f;
1066 g->aspect_ratios[i++] = 2.0f;
1067 g->aspect_ratios[i++] = 16.0f / 9.0f;
1068 g->aspect_ratios[i++] = PHI;
1069 g->aspect_ratios[i++] = 3.0f / 2.0f;
1070 g->aspect_ratios[i++] = 297.0f / 210.0f;
1071 g->aspect_ratios[i++] = sqrtf(2.0f);
1072 g->aspect_ratios[i++] = 4.0f / 3.0f;
1073 g->aspect_ratios[i++] = 1.0f;
1074}
1075
1076static void gui_init_positions(struct dt_iop_module_t *self)
1077{
1079
1080 dt_bauhaus_combobox_add(g->pos_h, _("center"));
1081 dt_bauhaus_combobox_add(g->pos_h, _("1/3"));
1082 dt_bauhaus_combobox_add(g->pos_h, _("3/8"));
1083 dt_bauhaus_combobox_add(g->pos_h, _("5/8"));
1084 dt_bauhaus_combobox_add(g->pos_h, _("2/3"));
1085 dt_bauhaus_combobox_add(g->pos_h, _("custom..."));
1086 dt_bauhaus_combobox_add(g->pos_v, _("center"));
1087 dt_bauhaus_combobox_add(g->pos_v, _("1/3"));
1088 dt_bauhaus_combobox_add(g->pos_v, _("3/8"));
1089 dt_bauhaus_combobox_add(g->pos_v, _("5/8"));
1090 dt_bauhaus_combobox_add(g->pos_v, _("2/3"));
1091 dt_bauhaus_combobox_add(g->pos_v, _("custom..."));
1092
1093 int i = 0;
1094 g->pos_h_ratios[i++] = 0.5f;
1095 g->pos_h_ratios[i++] = 1.0f / 3.0f;
1096 g->pos_h_ratios[i++] = 3.0f / 8.0f;
1097 g->pos_h_ratios[i++] = 5.0f / 8.0f;
1098 g->pos_h_ratios[i++] = 2.0f / 3.0f;
1099 i = 0;
1100 g->pos_v_ratios[i++] = 0.5f;
1101 g->pos_v_ratios[i++] = 1.0f / 3.0f;
1102 g->pos_v_ratios[i++] = 3.0f / 8.0f;
1103 g->pos_v_ratios[i++] = 5.0f / 8.0f;
1104 g->pos_v_ratios[i++] = 2.0f / 3.0f;
1105}
1106
1107void gui_init(struct dt_iop_module_t *self)
1108{
1111
1112 g->size = dt_bauhaus_slider_from_params(self, "size");
1114 dt_bauhaus_slider_set_format(g->size, "%");
1115 gtk_widget_set_tooltip_text(g->size, _("size of the border in percent of the full image"));
1116
1119 dt_bauhaus_widget_set_label(g->aspect, N_("aspect"));
1120 gtk_box_pack_start(GTK_BOX(self->gui->widget), g->aspect, TRUE, TRUE, 0);
1121 gui_init_aspect(self);
1122 g_signal_connect(G_OBJECT(g->aspect), "value-changed", G_CALLBACK(aspect_changed), self);
1123 gtk_widget_set_tooltip_text(g->aspect, _("select the aspect ratio or right click and type your own (w:h)"));
1124 g->aspect_slider = dt_bauhaus_slider_from_params(self, "aspect");
1125 gtk_widget_set_tooltip_text(g->aspect_slider, _("set the custom aspect ratio"));
1126
1127 g->aspect_orient = dt_bauhaus_combobox_from_params(self, "aspect_orient");
1128 dt_bauhaus_combobox_add(g->aspect_orient, _("auto"));
1129 dt_bauhaus_combobox_add(g->aspect_orient, _("portrait"));
1130 dt_bauhaus_combobox_add(g->aspect_orient, _("landscape"));
1131 gtk_widget_set_tooltip_text(g->aspect_orient, _("aspect ratio orientation of the image with border"));
1132
1135 dt_bauhaus_widget_set_label(g->pos_h, N_("horizontal position"));
1136 gtk_box_pack_start(GTK_BOX(self->gui->widget), g->pos_h, TRUE, TRUE, 0);
1137 g_signal_connect(G_OBJECT(g->pos_h), "value-changed", G_CALLBACK(position_h_changed), self);
1138 gtk_widget_set_tooltip_text(g->pos_h, _("select the horizontal position ratio relative to top "
1139 "or right click and type your own (y:h)"));
1140 g->pos_h_slider = dt_bauhaus_slider_from_params(self, "pos_h");
1141 gtk_widget_set_tooltip_text(g->pos_h_slider, _("custom horizontal position"));
1142
1145 dt_bauhaus_widget_set_label(g->pos_v, N_("vertical position"));
1146 gtk_box_pack_start(GTK_BOX(self->gui->widget), g->pos_v, TRUE, TRUE, 0);
1147 g_signal_connect(G_OBJECT(g->pos_v), "value-changed", G_CALLBACK(position_v_changed), self);
1148 gtk_widget_set_tooltip_text(g->pos_v, _("select the vertical position ratio relative to left "
1149 "or right click and type your own (x:w)"));
1150 g->pos_v_slider = dt_bauhaus_slider_from_params(self, "pos_v");
1151 gtk_widget_set_tooltip_text(g->pos_v_slider, _("custom vertical position"));
1152
1153 gui_init_positions(self);
1154
1155 g->frame_size = dt_bauhaus_slider_from_params(self, "frame_size");
1156 dt_bauhaus_slider_set_digits(g->frame_size, 4);
1157 dt_bauhaus_slider_set_format(g->frame_size, "%");
1158 gtk_widget_set_tooltip_text(g->frame_size, _("size of the frame line in percent of min border width"));
1159
1160 g->frame_offset = dt_bauhaus_slider_from_params(self, "frame_offset");
1161 dt_bauhaus_slider_set_digits(g->frame_offset, 4);
1162 dt_bauhaus_slider_set_format(g->frame_offset, "%");
1163 gtk_widget_set_tooltip_text(g->frame_offset, _("offset of the frame line beginning on picture side"));
1164
1165 GdkRGBA color = (GdkRGBA){.red = p->color[0], .green = p->color[1], .blue = p->color[2], .alpha = 1.0 };
1166
1167 GtkWidget *label, *box;
1168
1169 box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
1170 label = dt_iop_gui_reset_label_new(_("border color"), self, &p->color, 3 * sizeof(float));
1171 gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 0);
1172 g->colorpick = gtk_color_button_new_with_rgba(&color);
1173 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(g->colorpick), FALSE);
1174 gtk_color_button_set_title(GTK_COLOR_BUTTON(g->colorpick), _("select border color"));
1175 g_signal_connect(G_OBJECT(g->colorpick), "color-set", G_CALLBACK(colorpick_color_set), self);
1176 gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(g->colorpick), FALSE, TRUE, 0);
1177 g->border_picker = dt_color_picker_new(self, DT_COLOR_PICKER_POINT, box);
1178 gtk_widget_set_tooltip_text(GTK_WIDGET(g->border_picker), _("pick border color from image"));
1179 gtk_box_pack_start(GTK_BOX(self->gui->widget), box, TRUE, TRUE, 0);
1180
1181 box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
1182 label = dt_iop_gui_reset_label_new(_("frame line color"), self, &p->color, 3 * sizeof(float));
1183 gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 0);
1184 g->frame_colorpick = gtk_color_button_new_with_rgba(&color);
1185 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(g->frame_colorpick), FALSE);
1186 gtk_color_button_set_title(GTK_COLOR_BUTTON(g->frame_colorpick), _("select frame line color"));
1187 g_signal_connect(G_OBJECT(g->frame_colorpick), "color-set", G_CALLBACK(frame_colorpick_color_set), self);
1188 gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(g->frame_colorpick), FALSE, TRUE, 0);
1189 g->frame_picker = dt_color_picker_new(self, DT_COLOR_PICKER_POINT, box);
1190 gtk_widget_set_tooltip_text(GTK_WIDGET(g->frame_picker), _("pick frame line color from image"));
1191 gtk_box_pack_start(GTK_BOX(self->gui->widget), box, TRUE, TRUE, 0);
1192}
1193
1194
1196{
1197 dt_iop_default_init(self);
1198
1199 dt_iop_borders_params_t *defaults = self->default_params;
1200
1201 g_strlcpy(defaults->aspect_text, "constant border", sizeof(defaults->aspect_text));
1202 g_strlcpy(defaults->pos_h_text, "1/2", sizeof(defaults->pos_h_text));
1203 g_strlcpy(defaults->pos_v_text, "1/2", sizeof(defaults->pos_v_text));
1204}
1205
1206// clang-format off
1207// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1208// vim: shiftwidth=2 expandtab tabstop=2 cindent
1209// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1210// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_bauhaus_slider_set_digits(GtkWidget *widget, int val)
Definition bauhaus.c:3343
void dt_bauhaus_combobox_set_editable(GtkWidget *widget, int editable)
Definition bauhaus.c:1882
int dt_bauhaus_combobox_get(GtkWidget *widget)
Definition bauhaus.c:2136
int dt_bauhaus_combobox_length(GtkWidget *widget)
Definition bauhaus.c:1963
const char * dt_bauhaus_combobox_get_text(GtkWidget *widget)
Definition bauhaus.c:1970
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3331
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2090
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1504
GtkWidget * dt_bauhaus_combobox_new(dt_bauhaus_t *bh, dt_gui_module_t *self)
Definition bauhaus.c:1698
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3407
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
Definition bauhaus.c:1824
int operation_tags()
Definition borders.c:228
#define DT_IOP_BORDERS_ASPECT_COUNT
Definition borders.c:82
static void position_v_changed(GtkWidget *combo, dt_iop_module_t *self)
Definition borders.c:908
void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition borders.c:776
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)
Definition borders.c:298
const char ** description(struct dt_iop_module_t *self)
Definition borders.c:213
int default_group()
Definition borders.c:223
static int _borders_geometry_transform(const void *data, const dt_geometry_record_t *const record, dt_geometry_chain_t *chain, float *points, size_t points_count)
Definition borders.c:728
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)
Definition borders.c:278
static void gui_init_positions(struct dt_iop_module_t *self)
Definition borders.c:1076
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)
Definition borders.c:259
static void colorpick_color_set(GtkColorButton *widget, dt_iop_module_t *self)
Definition borders.c:948
static const dt_geometry_vtable_t _borders_geometry_vtable
Definition borders.c:756
static void _borders_map_size(const dt_iop_borders_data_t *const d, const dt_iop_roi_t *const roi_in, dt_iop_roi_t *roi_out)
Input rect -> output rect. THE size evaluator: modify_roi_out() below and the geometry record both ca...
Definition borders.c:330
#define DT_IOP_BORDERS_POSITION_H_COUNT
Definition borders.c:90
static int _borders_geometry_backtransform(const void *data, const dt_geometry_record_t *const record, dt_geometry_chain_t *chain, float *points, size_t points_count)
Definition borders.c:742
struct dt_iop_borders_params_t dt_iop_borders_data_t
Definition borders.c:205
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition borders.c:784
static void position_h_changed(GtkWidget *combo, dt_iop_module_t *self)
Definition borders.c:886
const char * name()
Definition borders.c:208
gboolean geometry_record(dt_iop_module_t *self, const void *params, dt_geometry_record_t *record)
Definition borders.c:762
void gui_update(struct dt_iop_module_t *self)
Definition borders.c:983
static void gui_init_aspect(struct dt_iop_module_t *self)
Definition borders.c:1043
static void aspect_changed(GtkWidget *combo, dt_iop_module_t *self)
Definition borders.c:864
void gui_init(struct dt_iop_module_t *self)
Definition borders.c:1107
__DT_CLONE_TARGETS__ void copy_image_with_border(float *out, const float *const in, const struct border_positions_t *binfo)
Definition borders.c:464
#define DT_IOP_BORDERS_POSITION_V_COUNT
Definition borders.c:91
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
Definition borders.c:930
void cleanup_global(dt_iop_module_so_t *module)
Definition borders.c:713
static void set_pixels(float *buf, const dt_aligned_pixel_t color, const int npixels)
Definition borders.c:438
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition borders.c:238
#define DT_IOP_BORDERS_ASPECT_ORIENTATION_AUTO
Definition borders.c:87
int flags()
Definition borders.c:233
#define DT_IOP_BORDERS_ASPECT_CONSTANT_VALUE
Definition borders.c:86
static void copy_pixels(float *out, const float *const in, const int npixels)
Definition borders.c:451
static void _borders_geometry_map_size(const void *data, const dt_iop_roi_t *const in, dt_iop_roi_t *out)
Definition borders.c:723
void init_presets(dt_iop_module_so_t *self)
Definition borders.c:796
static void frame_colorpick_color_set(GtkColorButton *widget, dt_iop_module_t *self)
Definition borders.c:966
void init(dt_iop_module_t *self)
Definition borders.c:1195
#define DT_IOP_BORDERS_ASPECT_ORIENTATION_LANDSCAPE
Definition borders.c:89
#define DT_IOP_BORDERS_ASPECT_ORIENTATION_PORTRAIT
Definition borders.c:88
#define DT_IOP_BORDERS_ASPECT_CONSTANT_IDX
Definition borders.c:84
int process(struct 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)
Definition borders.c:518
#define DT_IOP_BORDERS_ASPECT_IMAGE_IDX
Definition borders.c:83
#define DT_IOP_BORDERS_ASPECT_IMAGE_VALUE
Definition borders.c:85
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition borders.c:790
void init_global(dt_iop_module_so_t *module)
Definition borders.c:703
int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
Definition borders.c:597
void color_picker_apply(dt_iop_module_t *self, GtkWidget *picker, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition borders.c:820
void modify_roi_in(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t *roi_out, dt_iop_roi_t *roi_in)
Definition borders.c:388
static void _borders_offset(const dt_iop_borders_data_t *const d, const dt_iop_roi_t *const in, const dt_iop_roi_t *const out, int *left, int *top)
The translation the border applies, from the two rects it sits between.
Definition borders.c:252
void modify_roi_out(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece, dt_iop_roi_t *roi_out, const dt_iop_roi_t *roi_in)
Definition borders.c:380
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params, const int new_version)
Definition borders.c:143
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
@ IOP_CS_RGB
void dt_iop_color_picker_reset(dt_iop_module_t *module, gboolean keep)
GtkWidget * dt_color_picker_new(dt_iop_module_t *module, dt_iop_color_picker_kind_t kind, GtkWidget *w)
@ DT_COLOR_PICKER_POINT
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
const dt_colormatrix_t dt_aligned_pixel_t out
const float top
static const int row
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
Definition darktable.c:646
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:43
Where things are on the image, answered without a pipeline.
GdkRGBA color[]
Definition geotagging.c:541
void dt_gui_presets_add_generic(const char *name, dt_dev_operation_t op, const int32_t version, const void *params, const int32_t params_size, const int32_t enabled)
#define DT_GUI_MODULE(x)
__DT_CLONE_TARGETS__ void dt_iop_image_fill(float *const buf, const float fill_value, const size_t width, const size_t height, const size_t ch)
Definition imagebuf.c:218
void dt_iop_default_init(dt_iop_module_t *module)
Definition imageop.c:308
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:1893
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:188
@ IOP_FLAGS_TILING_FULL_ROI
Definition imageop.h:190
@ IOP_GROUP_EFFECTS
Definition imageop.h:161
@ IOP_TAG_DECORATION
Definition imageop.h:171
@ IOP_TAG_DISTORT
Definition imageop.h:170
GtkWidget * dt_iop_gui_reset_label_new(const gchar *label, dt_iop_module_t *module, void *param, int param_size)
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
GtkWidget * dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *param)
static dt_iop_gui_data_t * dt_iop_gui_data(const struct dt_iop_module_t *m)
The module's GUI data blob, NULL-safe for headless callers: IOP process() implementations read it for...
Definition imageop_gui.h:81
#define IOP_GUI_ALLOC(module)
Definition imageop_gui.h:93
void *const ovoid
@ DT_DEBUG_OPENCL
Definition logging.h:57
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.
float *const restrict const size_t k
#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
#define PHI
Definition math.h:69
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
Definition mem_alloc.h:214
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
Definition mem_alloc.h:225
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
size_t size
Definition mipmap_cache.c:3
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2554
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2448
int dt_opencl_enqueue_copy_image(const int devid, cl_mem src, cl_mem dst, size_t *orig_src, size_t *orig_dst, size_t *region)
Definition opencl.c:2679
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2491
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
Definition opencl.c:2545
#define ROUNDUPDHT(a, b)
Definition opencl.h:86
#define ROUNDUPDWD(a, b)
Definition opencl.h:85
#define __OMP_SIMD__(...)
Definition openmp.h:99
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
#define __OMP_PARALLEL_FOR_SIMD__(...)
Definition openmp.h:96
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
dt_aligned_pixel_t flcolor
Definition borders.c:419
dt_aligned_pixel_t bcolor
Definition borders.c:418
struct dt_iop_module_t *void * data
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
A module's geometry, evaluated. Pure functions of the record's own data.
Definition geometry.h:75
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
GtkWidget * frame_offset
Definition borders.c:137
GtkWidget * frame_picker
Definition borders.c:139
GtkWidget * border_picker
Definition borders.c:132
GtkWidget * frame_colorpick
Definition borders.c:138
GtkWidget * pos_v_slider
Definition borders.c:130
GtkWidget * aspect_slider
Definition borders.c:125
GtkWidget * aspect_orient
Definition borders.c:126
GtkWidget * pos_h_slider
Definition borders.c:128
GtkWidget * widget
Definition imageop_gui.h:47
dt_dev_operation_t op
Definition imageop.h:235
dt_iop_global_data_t * data
Definition imageop.h:238
dt_iop_params_t * default_params
Definition imageop.h:333
struct dt_iop_module_gui_t * gui
Definition imageop.h:346
struct dt_develop_t * dev
Definition imageop.h:311
dt_iop_global_data_t * global_data
Definition imageop.h:337
dt_aligned_pixel_t picked_color
Definition imageop.h:287
dt_iop_params_t * params
Definition imageop.h:333
Region of interest passed through the pixelpipe.
Definition format.h:49
double scale
Definition format.h:51
int width
Definition format.h:50
int height
Definition format.h:50
#define __DT_CLONE_TARGETS__
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
gboolean dt_gui_widgets_suppressed(void)
#define dt_gui_freeze_begin()
#define dt_gui_freeze_end()
#define DT_GUI_BOX_SPACING