Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
watermark.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2010 Bruce Guenter.
4 Copyright (C) 2010-2011 Henrik Andersson.
5 Copyright (C) 2010-2013, 2016 johannes hanika.
6 Copyright (C) 2010, 2012 José Carlos García Sogo.
7 Copyright (C) 2010-2019 Tobias Ellinghaus.
8 Copyright (C) 2011 Antony Dovgal.
9 Copyright (C) 2011-2012 Jérémy Rosen.
10 Copyright (C) 2011 Olivier Tribout.
11 Copyright (C) 2011 Robert Bieber.
12 Copyright (C) 2012 Christian Tellefsen.
13 Copyright (C) 2012 Edouard Gomez.
14 Copyright (C) 2012 Jean-Sébastien Pédron.
15 Copyright (C) 2012 Richard Wonka.
16 Copyright (C) 2012 Simon Spannagel.
17 Copyright (C) 2012, 2014 Ulrich Pegelow.
18 Copyright (C) 2013 bleader.
19 Copyright (C) 2013 hal.
20 Copyright (C) 2013-2015, 2018-2022 Pascal Obry.
21 Copyright (C) 2013-2016 Roman Lebedev.
22 Copyright (C) 2015 Pedro Côrte-Real.
23 Copyright (C) 2016, 2020, 2022 Aldric Renaudin.
24 Copyright (C) 2017 luzpaz.
25 Copyright (C) 2018-2020, 2023, 2025-2026 Aurélien PIERRE.
26 Copyright (C) 2018-2019 Edgardo Hoszowski.
27 Copyright (C) 2018-2019, 2021 Heiko Bauke.
28 Copyright (C) 2018 Maurizio Paglia.
29 Copyright (C) 2018 rawfiner.
30 Copyright (C) 2019-2020, 2022 Philippe Weyland.
31 Copyright (C) 2019 Robert Bridge.
32 Copyright (C) 2020 Chris Elston.
33 Copyright (C) 2020-2022 Diederik Ter Rahe.
34 Copyright (C) 2020 Hanno Schwalm.
35 Copyright (C) 2020 hatsunearu.
36 Copyright (C) 2020 Hubert Kowalski.
37 Copyright (C) 2020-2021 Marco.
38 Copyright (C) 2020-2021 Ralf Brown.
39 Copyright (C) 2021 Benjamin Grimm-Lebsanft.
40 Copyright (C) 2022 Martin Bařinka.
41 Copyright (C) 2022 Philipp Lutz.
42 Copyright (C) 2023 Luca Zulberti.
43 Copyright (C) 2023 Ricky Moon.
44
45 darktable is free software: you can redistribute it and/or modify
46 it under the terms of the GNU General Public License as published by
47 the Free Software Foundation, either version 3 of the License, or
48 (at your option) any later version.
49
50 darktable is distributed in the hope that it will be useful,
51 but WITHOUT ANY WARRANTY; without even the implied warranty of
52 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
53 GNU General Public License for more details.
54
55 You should have received a copy of the GNU General Public License
56 along with darktable. If not, see <http://www.gnu.org/licenses/>.
57*/
58
60#include "common/paths.h" // DT_PATH_MAX
61#include "system/macros.h"
63#include "system/mem_alloc.h"
64#include "system/openmp.h"
66#include "common/conf.h"
67#include "widgets/bauhaus.h"
68#include "common/imagebuf.h"
69#include "common/variables.h"
70#include "develop/develop.h"
71#include "develop/imageop.h"
72#include "develop/imageop_gui.h"
73#include "widgets/button.h"
74#include "develop/imageop_gui.h"
76
78#include "iop/iop_api.h"
79#include <assert.h>
80#include <gtk/gtk.h>
81#include <inttypes.h>
82#include <math.h>
83#include <stdlib.h>
84#include <string.h>
85
86#include <librsvg/rsvg.h>
87// ugh, ugly hack. why do people break stuff all the time?
88#ifndef RSVG_CAIRO_H
89#include <librsvg/rsvg-cairo.h>
90#endif
91
93#include "common/utility.h"
95#include "widgets/container.h"
96#include "widgets/label.h"
97
98/* rsvg, and the cairo underneath it, are not thread safe -- notably around font handling.
99 * This module is the only place in the pixel path that drives them, so the lock lives here
100 * rather than in a process-wide "plugin" mutex shared with unrelated subsystems.
101 *
102 * Initialised through pthread_once rather than a static initialiser, so it gets the same
103 * recursive attribute every other lock in the tree gets from
104 * dt_pthread_mutex_init(..., NULL) -- a static PTHREAD_MUTEX_INITIALIZER cannot carry one.
105 * See doc/lock-audit.md. */
106static dt_pthread_mutex_t _rsvg_lock;
107static pthread_once_t _rsvg_lock_once = PTHREAD_ONCE_INIT;
108
109static void _rsvg_lock_init(void)
110{
112}
113
115
116// gchar *checksum = g_compute_checksum_for_data(G_CHECKSUM_MD5,data,length);
117
119{
120 DT_SCALE_IMAGE = 0, // $DESCRIPTION: "image"
121 DT_SCALE_LARGER_BORDER = 1, // $DESCRIPTION: "larger border"
122 DT_SCALE_SMALLER_BORDER = 2 // $DESCRIPTION: "smaller border"
124
126{
127 DT_WTM_SVG = 0, // $DESCRIPTION: "vector .svg"
128 DT_WTM_PNG = 1 // $DESCRIPTION: "raster .png"
130
132{
134 float opacity; // $MIN: 0.0 $MAX: 100.0 $DEFAULT: 100.0
136 float scale; // $MIN: 1.0 $MAX: 500.0 $DEFAULT: 100.0
138 float xoffset; // $MIN: -1.0 $MAX: 1.0, 0.001 $DEFAULT: 0.0 $DESCRIPTION: "x offset"
140 float yoffset; // $MIN: -1.0 $MAX: 1.0, 0.001 $DEFAULT: 0.0 $DESCRIPTION: "y offset"
142 int alignment; // $DEFAULT: 4
144 float rotate; // $MIN: -180.0 $MAX: 180.0 $DEFAULT: 0.0 $DESCRIPTION: "rotation"
145 dt_iop_watermark_base_scale_t sizeto; // $DEFAULT: DT_SCALE_IMAGE $DESCRIPTION: "scale on"
146 char filename[64];
147 /* simple text */
148 char text[512];
149 /* text color */
150 float color[3]; // $DEFAULT: 0.0
151 /* text font */
152 char font[64];
154
169
171{
172 GtkWidget *watermarks; // watermark
173 GList *watermarks_filenames; // the actual filenames
174 GtkWidget *refresh; // refresh watermarks...
175 GtkWidget *align[9]; // Alignment buttons
176 GtkWidget *opacity, *scale, *x_offset, *y_offset; // opacity, scale, xoffs, yoffs
177 GtkWidget *sizeto; // relative size to
184
185int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
186 void *new_params, const int new_version)
187{
188 if(old_version == 1 && new_version == 5)
189 {
190 typedef struct dt_iop_watermark_params_v1_t
191 {
193 float opacity;
195 float scale;
197 float xoffset;
199 float yoffset;
201 int alignment;
202 char filename[64];
203 } dt_iop_watermark_params_v1_t;
204
205 dt_iop_watermark_params_v1_t *o = (dt_iop_watermark_params_v1_t *)old_params;
208
209 *n = *d; // start with a fresh copy of default parameters
210
211 n->opacity = o->opacity;
212 n->scale = o->scale;
213 n->xoffset = o->xoffset;
214 n->yoffset = o->yoffset;
215 n->alignment = o->alignment;
216 n->rotate = 0.0;
217 n->sizeto = DT_SCALE_IMAGE;
218 g_strlcpy(n->filename, o->filename, sizeof(n->filename));
219 g_strlcpy(n->text, "", sizeof(n->text));
220 g_strlcpy(n->font, "DejaVu Sans 10", sizeof(n->font));
221 n->color[0] = n->color[1] = n->color[2] = 0;
222 return 0;
223 }
224 else if(old_version == 2 && new_version == 5)
225 {
226 typedef struct dt_iop_watermark_params_v2_t
227 {
229 float opacity;
231 float scale;
233 float xoffset;
235 float yoffset;
237 int alignment;
239 char filename[64];
240 } dt_iop_watermark_params_v2_t;
241
242 dt_iop_watermark_params_v2_t *o = (dt_iop_watermark_params_v2_t *)old_params;
245
246 *n = *d; // start with a fresh copy of default parameters
247
248 n->opacity = o->opacity;
249 n->scale = o->scale;
250 n->xoffset = o->xoffset;
251 n->yoffset = o->yoffset;
252 n->alignment = o->alignment;
253 n->rotate = 0.0;
254 n->sizeto = DT_SCALE_IMAGE;
255 g_strlcpy(n->filename, o->filename, sizeof(n->filename));
256 g_strlcpy(n->text, "", sizeof(n->text));
257 g_strlcpy(n->font, "DejaVu Sans 10", sizeof(n->font));
258 n->color[0] = n->color[1] = n->color[2] = 0;
259 return 0;
260 }
261 else if(old_version == 3 && new_version == 5)
262 {
263 typedef struct dt_iop_watermark_params_v3_t
264 {
266 float opacity;
268 float scale;
270 float xoffset;
272 float yoffset;
274 int alignment;
276 float rotate;
278 char filename[64];
279 } dt_iop_watermark_params_v3_t;
280
281 dt_iop_watermark_params_v3_t *o = (dt_iop_watermark_params_v3_t *)old_params;
284
285 *n = *d; // start with a fresh copy of default parameters
286
287 n->opacity = o->opacity;
288 n->scale = o->scale;
289 n->xoffset = o->xoffset;
290 n->yoffset = o->yoffset;
291 n->alignment = o->alignment;
292 n->rotate = o->rotate;
293 n->sizeto = o->sizeto;
294 g_strlcpy(n->filename, o->filename, sizeof(n->filename));
295 g_strlcpy(n->text, "", sizeof(n->text));
296 g_strlcpy(n->font, "DejaVu Sans 10", sizeof(n->font));
297 n->color[0] = n->color[1] = n->color[2] = 0;
298 return 0;
299 }
300 else if(old_version == 4 && new_version == 5)
301 {
302 typedef struct dt_iop_watermark_params_v4_t
303 {
305 float opacity;
307 float scale;
309 float xoffset;
311 float yoffset;
313 int alignment;
315 float rotate;
317 char filename[64];
318 /* simple text */
319 char text[64];
320 /* text color */
321 float color[3];
322 /* text font */
323 char font[64];
324 } dt_iop_watermark_params_v4_t;
325
326 dt_iop_watermark_params_v4_t *o = (dt_iop_watermark_params_v4_t *)old_params;
329
330 *n = *d; // start with a fresh copy of default parameters
331
332 n->opacity = o->opacity;
333 n->scale = o->scale;
334 n->xoffset = o->xoffset;
335 n->yoffset = o->yoffset;
336 n->alignment = o->alignment;
337 n->rotate = o->rotate;
338 n->sizeto = o->sizeto;
339 g_strlcpy(n->filename, o->filename, sizeof(n->filename));
340 g_strlcpy(n->text, o->text, sizeof(n->text));
341 g_strlcpy(n->font, o->font, sizeof(n->font));
342 n->color[0] = o->color[0];
343 n->color[1] = o->color[1];
344 n->color[2] = o->color[2];
345 return 0;
346 }
347 return 1;
348}
349
350
351const char *name()
352{
353 return _("_Watermark");
354}
355
356const char **description(struct dt_iop_module_t *self)
357{
358 return dt_iop_set_description(self, _("overlay an SVG watermark like a signature on the picture"),
359 _("creative"),
360 _("non-linear, RGB, display-referred"),
361 _("non-linear, RGB"),
362 _("non-linear, RGB, display-referred"));
363}
364
369
371{
372 return IOP_GROUP_EFFECTS;
373}
374
376{
377 return IOP_TAG_DECORATION;
378}
379
381{
382 return IOP_CS_RGB;
383}
384
387{
388 default_input_format(self, pipe, piece, dsc);
389 dsc->channels = 4;
390 dsc->datatype = TYPE_FLOAT;
391}
392
393// sets text / color / font widgets sensitive based on watermark file type
395{
396 const gchar *extension = strrchr(filename, '.');
398 {
399 const gboolean active = !g_ascii_strcasecmp(extension, ".svg");
400 gtk_widget_set_sensitive(GTK_WIDGET(g->colorpick), active);
401 gtk_widget_set_sensitive(GTK_WIDGET(g->color_picker_button), active);
402 gtk_widget_set_sensitive(GTK_WIDGET(g->text), active);
403 gtk_widget_set_sensitive(GTK_WIDGET(g->fontsel), active);
404 }
405}
406
408{
409 int i = 0;
410 for(const GList *iter = g->watermarks_filenames; iter; iter = g_list_next(iter))
411 {
412 if(!g_strcmp0((gchar *)iter->data, text))
413 {
414 dt_bauhaus_combobox_set(g->watermarks, i);
416 return;
417 }
418 i++;
419 }
420}
421
422// replace < and > with &lt; and &gt;. any more? Yes! & -> &amp;
423static gchar *_string_escape(const gchar *string)
424{
425 gchar *result, *result_old;
426 result = dt_util_str_replace(string, "&", "&amp;");
427
428 result_old = result;
429 result = dt_util_str_replace(result_old, "<", "&lt;");
430 dt_free(result_old);
431
432 result_old = result;
433 result = dt_util_str_replace(result_old, ">", "&gt;");
434 dt_free(result_old);
435
436 return result;
437}
438
439static gchar *_string_substitute(gchar *string, const gchar *search, const gchar *replace)
440{
441 gchar *_replace = _string_escape(replace);
442 gchar *result = dt_util_str_replace(string, search, _replace);
443 dt_free(_replace);
444 dt_free(string); // dt_util_str_replace always returns a new string, and we don't need the original after this func
445 return result;
446}
447
449 const dt_image_t *image, const gchar *filename)
450{
451 gchar *svgdata = NULL;
452 gsize length = 0;
453 if(g_file_get_contents(filename, &svgdata, &length, NULL))
454 {
455 // File is loaded lets substitute strings if found...
456 // Simple text from watermark module
457 gchar buffer[1024];
458
459 // substitute $(WATERMARK_TEXT)
460 if(data->text[0])
461 {
462 g_strlcpy(buffer, data->text, sizeof(buffer));
463 svgdata = _string_substitute(svgdata, "$(WATERMARK_TEXT)", buffer);
464 }
465 // apply font style substitutions
466 PangoFontDescription *font = pango_font_description_from_string(data->font);
467 const PangoStyle font_style = pango_font_description_get_style(font);
468 const int font_weight = (int)pango_font_description_get_weight(font);
469
470 g_strlcpy(buffer, pango_font_description_get_family(font), sizeof(buffer));
471 svgdata = _string_substitute(svgdata, "$(WATERMARK_FONT_FAMILY)", buffer);
472
473 switch(font_style)
474 {
475 case PANGO_STYLE_OBLIQUE:
476 g_strlcpy(buffer, "oblique", sizeof(buffer));
477 break;
478 case PANGO_STYLE_ITALIC:
479 g_strlcpy(buffer, "italic", sizeof(buffer));
480 break;
481 default:
482 g_strlcpy(buffer, "normal", sizeof(buffer));
483 break;
484 }
485 svgdata = _string_substitute(svgdata, "$(WATERMARK_FONT_STYLE)", buffer);
486
487 g_snprintf(buffer, sizeof(buffer), "%d", font_weight);
488 svgdata = _string_substitute(svgdata, "$(WATERMARK_FONT_WEIGHT)", buffer);
489
490 pango_font_description_free(font);
491
492 // watermark color
493 GdkRGBA c = { data->color[0], data->color[1], data->color[2], 1.0f };
494 g_strlcpy(buffer, gdk_rgba_to_string(&c), sizeof(buffer));
495 svgdata = _string_substitute(svgdata, "$(WATERMARK_COLOR)", buffer);
496
497 // standard calculation on the remaining variables
499 dt_variables_params_t *params;
501 gboolean from_cache = FALSE;
502 char image_path[DT_PATH_MAX] = { 0 };
503 dt_image_full_path(image->id, image_path, sizeof(image_path), &from_cache, __FUNCTION__);
504 params->filename = image_path;
505 params->jobcode = "infos";
506 params->sequence = 0;
507 params->imgid = image->id;
509 gchar *svgdoc = dt_variables_expand(params, svgdata, FALSE); // returns a new string
511 dt_free(svgdata); // free the old one
512 svgdata = svgdoc; // and make the expanded string our result
513 }
514 return svgdata;
515}
516
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;
524 float *in = (float *)ivoid;
525 float *out = (float *)ovoid;
526 const int ch = 4;
527 const float angle = (M_PI / 180) * (-data->rotate);
528
529 gchar configdir[DT_PATH_MAX] = { 0 };
530 gchar datadir[DT_PATH_MAX] = { 0 };
531 gchar *filename;
532 dt_loc_get_datadir(datadir, sizeof(datadir));
533 dt_loc_get_user_config_dir(configdir, sizeof(configdir));
534 g_strlcat(datadir, "/watermarks/", sizeof(datadir));
535 g_strlcat(configdir, "/watermarks/", sizeof(configdir));
536 g_strlcat(datadir, data->filename, sizeof(datadir));
537 g_strlcat(configdir, data->filename, sizeof(configdir));
538
539 if(g_file_test(configdir, G_FILE_TEST_EXISTS))
540 filename = configdir;
541 else if(g_file_test(datadir, G_FILE_TEST_EXISTS))
542 filename = datadir;
543 else
544 {
545 dt_iop_image_copy_by_size(ovoid, ivoid, roi_out->width, roi_out->height, ch);
546 return 0;
547 }
548
549 // find out the watermark type
551 const gchar *extension = strrchr(data->filename, '.');
553 {
554 if(!g_ascii_strcasecmp(extension, ".svg"))
556 else if(!g_ascii_strcasecmp(extension, ".png"))
558 else // this should not happen
559 {
560 dt_iop_image_copy_by_size(ovoid, ivoid, roi_out->width, roi_out->height, ch);
561 return 0;
562 }
563 }
564 else
565 {
566 dt_iop_image_copy_by_size(ovoid, ivoid, roi_out->width, roi_out->height, ch);
567 return 0;
568 }
569
570 /* Load svg if not loaded */
571 gchar *svgdoc = NULL;
572 if(type == DT_WTM_SVG)
573 {
574 svgdoc = _watermark_get_svgdoc(self, data, &pipe->dev->image_storage, filename);
575 if(IS_NULL_PTR(svgdoc))
576 {
577 dt_iop_image_copy_by_size(ovoid, ivoid, roi_out->width, roi_out->height, ch);
578 return 0;
579 }
580 }
581
582 /* setup stride for performance */
583 const int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, roi_out->width);
584 if(stride == -1)
585 {
586 fprintf(stderr, "[watermark] cairo stride error\n");
587 dt_iop_image_copy_by_size(ovoid, ivoid, roi_out->width, roi_out->height, ch);
588 return 0;
589 }
590
591 /* create a cairo memory surface that is later used for reading watermark overlay data */
592 guint8 *image = (guint8 *)g_try_malloc0_n(roi_out->height, stride);
593 if(IS_NULL_PTR(image))
594 {
595 dt_iop_image_copy_by_size(ovoid, ivoid, roi_out->width, roi_out->height, ch);
596 return 1;
597 }
598 cairo_surface_t *surface = cairo_image_surface_create_for_data(image, CAIRO_FORMAT_ARGB32, roi_out->width,
599 roi_out->height, stride);
600 if(cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS)
601 {
602 fprintf(stderr, "[watermark] cairo surface error: %s\n",
603 cairo_status_to_string(cairo_surface_status(surface)));
604 dt_free(image);
605 dt_iop_image_copy_by_size(ovoid, ivoid, roi_out->width, roi_out->height, ch);
606 return 0;
607 }
608
609 // rsvg (or some part of cairo which is used underneath) isn't thread safe, for example when
610 // handling fonts. This lock belongs to this module: it is the only place in the pixel path
611 // that drives rsvg, and it used to share the process-wide "plugin" mutex with rawspeed's
612 // metadata init and the export filename allocator -- so a watermark render blocked an
613 // unrelated export from picking a filename. See doc/lock-audit.md.
614 pthread_once(&_rsvg_lock_once, _rsvg_lock_init);
616
617 RsvgHandle *svg = NULL;
618 if(type == DT_WTM_SVG)
619 {
620 /* create the rsvghandle from parsed svg data */
621 GError *error = NULL;
622 svg = rsvg_handle_new_from_data((const guint8 *)svgdoc, strlen(svgdoc), &error);
623 dt_free(svgdoc);
624 if(IS_NULL_PTR(svg) || error)
625 {
626 cairo_surface_destroy(surface);
627 dt_free(image);
628 dt_iop_image_copy_by_size(ovoid, ivoid, roi_out->width, roi_out->height, ch);
630 fprintf(stderr, "[watermark] error processing svg file: %s\n", error->message);
631 g_error_free(error);
632 return 0;
633 }
634 }
635
636 // we use a second surface
637 guint8 *image_two = NULL;
638 cairo_surface_t *surface_two = NULL;
639
640 /* get the dimension of svg or png */
641 RsvgDimensionData dimension;
642 switch(type)
643 {
644 case DT_WTM_SVG:
646 break;
647 case DT_WTM_PNG:
648 // load png into surface 2
649 surface_two = cairo_image_surface_create_from_png(filename);
650 if((cairo_surface_status(surface_two) != CAIRO_STATUS_SUCCESS))
651 {
652 fprintf(stderr, "[watermark] cairo png surface 2 error: %s\n",
653 cairo_status_to_string(cairo_surface_status(surface_two)));
654 cairo_surface_destroy(surface);
655 dt_free(image);
656 dt_iop_image_copy_by_size(ovoid, ivoid, roi_out->width, roi_out->height, ch);
658 return 0;
659 }
660 dimension.width = cairo_image_surface_get_width(surface_two);
661 dimension.height = cairo_image_surface_get_height(surface_two);
662 break;
663 }
664
665 // if no text is given dimensions are null
666 if(!dimension.width) dimension.width = 1;
667 if(!dimension.height) dimension.height = 1;
668
669 // width/height of current (possibly cropped) image
670 const float iw = piece->buf_in.width;
671 const float ih = piece->buf_in.height;
672 const float uscale = data->scale / 100.0f; // user scale, from GUI in percent
673
674 // wbase, hbase are the base width and height, this is the multiplicator used for the offset computing
675 // scale is the scale of the watermark itself and is used only to render it.
676
677 float wbase, hbase, scale;
678
679 if(data->sizeto == DT_SCALE_IMAGE)
680 {
681 // in image mode, the wbase and hbase are just the image width and height
682 wbase = iw;
683 hbase = ih;
684 if(dimension.width > dimension.height)
685 scale = (iw * roi_out->scale) / dimension.width;
686 else
687 scale = (ih * roi_out->scale) / dimension.height;
688 }
689 else
690 {
691 // in larger/smaller side mode, set wbase and hbase to the largest or smallest side of the image
692 const float larger = dimension.width > dimension.height
693 ? (float)dimension.width
694 : (float)dimension.height;
695
696 if(iw > ih)
697 {
698 wbase = hbase = (data->sizeto == DT_SCALE_LARGER_BORDER) ? iw : ih;
699 scale = (data->sizeto == DT_SCALE_LARGER_BORDER) ? (iw / larger) : (ih / larger);
700 }
701 else
702 {
703 wbase = hbase = (data->sizeto == DT_SCALE_SMALLER_BORDER) ? iw : ih;
704 scale = (data->sizeto == DT_SCALE_SMALLER_BORDER) ? (iw / larger) : (ih / larger);
705 }
706 scale *= roi_out->scale;
707 }
708
709 scale *= uscale;
710
711 // compute the width and height of the SVG object in image dimension. This is only used to properly
712 // layout the watermark based on the alignment.
713
714 float svg_width, svg_height;
715
716 if(dimension.width > dimension.height)
717 {
718 if(data->sizeto == DT_SCALE_IMAGE || (iw > ih && data->sizeto == DT_SCALE_LARGER_BORDER)
719 || (iw < ih && data->sizeto == DT_SCALE_SMALLER_BORDER))
720 {
721 svg_width = iw * uscale;
722 svg_height = dimension.height * (svg_width / dimension.width);
723 }
724 else
725 {
726 svg_width = ih * uscale;
727 svg_height = dimension.height * (svg_width / dimension.width);
728 }
729 }
730 else
731 {
732 if(data->sizeto == DT_SCALE_IMAGE || (ih > iw && data->sizeto == DT_SCALE_LARGER_BORDER)
733 || (ih < iw && data->sizeto == DT_SCALE_SMALLER_BORDER))
734 {
735 svg_height = ih * uscale;
736 svg_width = dimension.width * (svg_height / dimension.height);
737 }
738 else
739 {
740 svg_height = iw * uscale;
741 svg_width = dimension.width * (svg_height / dimension.height);
742 }
743 }
744
745 /* For the rotation we need an extra cairo image as rotations are buggy via rsvg_handle_render_cairo.
746 distortions and blurred images are obvious but you also can easily have crashes.
747 */
748
749 float svg_offset_x = 0;
750 float svg_offset_y = 0;
751 if(type == DT_WTM_SVG)
752 {
753 /* the svg_offsets allow safe text boxes as they might render out of the dimensions */
754 svg_offset_x = ceilf(3.0f * scale);
755 svg_offset_y = ceilf(3.0f * scale);
756
757 const int watermark_width = (int)((dimension.width * scale) + 3* svg_offset_x);
758 const int watermark_height = (int)((dimension.height * scale) + 3* svg_offset_y) ;
759
760 const int stride_two = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, watermark_width);
761 image_two = (guint8 *)g_try_malloc0_n(watermark_height, stride_two);
762 if(IS_NULL_PTR(image_two))
763 {
764 cairo_surface_destroy(surface);
765 g_object_unref(svg);
766 dt_free(image);
767 dt_iop_image_copy_by_size(ovoid, ivoid, roi_out->width, roi_out->height, ch);
769 return 1;
770 }
771 surface_two = cairo_image_surface_create_for_data(image_two, CAIRO_FORMAT_ARGB32, watermark_width,
772 watermark_height, stride_two);
773 if(cairo_surface_status(surface_two) != CAIRO_STATUS_SUCCESS)
774 {
775 fprintf(stderr, "[watermark] cairo surface 2 error: %s\n",
776 cairo_status_to_string(cairo_surface_status(surface_two)));
777 cairo_surface_destroy(surface);
778 g_object_unref(svg);
779 dt_free(image);
780 dt_free(image_two);
781 dt_iop_image_copy_by_size(ovoid, ivoid, roi_out->width, roi_out->height, ch);
783 return 0;
784 }
785 }
786
787 /* create cairo context and setup transformation/scale */
788 cairo_t *cr = cairo_create(surface);
789 /* create cairo context for the scaled watermark */
790 cairo_t *cr_two = cairo_create(surface_two);
791
792 // compute bounding box of rotated watermark
793 const float bb_width = fabsf(svg_width * cosf(angle)) + fabsf(svg_height * sinf(angle));
794 const float bb_height = fabsf(svg_width * sinf(angle)) + fabsf(svg_height * cosf(angle));
795 const float bX = bb_width / 2.0f - svg_width / 2.0f;
796 const float bY = bb_height / 2.0f - svg_height / 2.0f;
797
798 // compute translation for the given alignment in image dimension
799
800 float ty = 0, tx = 0;
801 if(data->alignment >= 0 && data->alignment < 3) // Align to verttop
802 ty = bY;
803 else if(data->alignment >= 3 && data->alignment < 6) // Align to vertcenter
804 ty = (ih / 2.0f) - (svg_height / 2.0f);
805 else if(data->alignment >= 6 && data->alignment < 9) // Align to vertbottom
806 ty = ih - svg_height - bY;
807
808 if(data->alignment == 0 || data->alignment == 3 || data->alignment == 6)
809 tx = bX;
810 else if(data->alignment == 1 || data->alignment == 4 || data->alignment == 7)
811 tx = (iw / 2.0f) - (svg_width / 2.0f);
812 else if(data->alignment == 2 || data->alignment == 5 || data->alignment == 8)
813 tx = iw - svg_width - bX;
814
815 // translate to position
816 cairo_translate(cr, -roi_in->x, -roi_in->y);
817
818 // add translation for the given value in GUI (xoffset,yoffset)
819 tx += data->xoffset * wbase;
820 ty += data->yoffset * hbase;
821
822 cairo_translate(cr, tx * roi_out->scale, ty * roi_out->scale);
823
824 // compute the center of the svg to rotate from the center
825 const float cX = svg_width / 2.0f * roi_out->scale;
826 const float cY = svg_height / 2.0f * roi_out->scale;
827
828 cairo_translate(cr, cX, cY);
829 cairo_rotate(cr, angle);
830 cairo_translate(cr, -cX, -cY);
831
832 // now set proper scale and translationfor the watermark itself
833 cairo_translate(cr_two, svg_offset_x, svg_offset_y);
834
835 switch(type)
836 {
837 case DT_WTM_SVG:
838 cairo_scale(cr_two, scale, scale);
839 /* render svg into surface*/
840 dt_render_svg(svg, cr_two, dimension.width, dimension.height, 0, 0);
841 break;
842 case DT_WTM_PNG:
843 cairo_scale(cr, scale, scale);
844 break;
845 }
846 cairo_surface_flush(surface_two);
847
848 // paint the watermark
849 cairo_set_source_surface(cr, surface_two, -svg_offset_x, -svg_offset_y);
850 cairo_paint(cr);
851
852 // no more non-thread safe rsvg usage
854
855 cairo_destroy(cr);
856 cairo_destroy(cr_two);
857
858 /* ensure that all operations on surface finishing up */
859 cairo_surface_flush(surface);
860
861 /* render surface on output */
862 guint8 *sd = image;
863 const float opacity = data->opacity / 100.0f;
865 for(int j = 0; j < roi_out->height * roi_out->width; j++)
866 {
867 float *const i = in + ch*j;
868 float *const o = out + ch*j;
869 guint8 *const s = sd + 4*j;
870 const float alpha = (s[3] / 255.0f) * opacity;
871 /* svg uses a premultiplied alpha, so only use opacity for the blending */
872 o[0] = ((1.0f - alpha) * i[0]) + (opacity * (s[2] / 255.0f));
873 o[1] = ((1.0f - alpha) * i[1]) + (opacity * (s[1] / 255.0f));
874 o[2] = ((1.0f - alpha) * i[2]) + (opacity * (s[0] / 255.0f));
875 o[3] = in[3];
876 }
877
878 /* clean up */
879 cairo_surface_destroy(surface);
880 cairo_surface_destroy(surface_two);
881 dt_free(image);
882 if(type == DT_WTM_SVG)
883 {
884 dt_free(image_two);
885 g_object_unref(svg);
886 }
887
888 return 0;
889}
890
891static void watermark_callback(GtkWidget *tb, gpointer user_data)
892{
893 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
895
896 if(dt_gui_widgets_suppressed()) return;
898 memset(p->filename, 0, sizeof(p->filename));
899 int n = dt_bauhaus_combobox_get(g->watermarks);
900 g_strlcpy(p->filename, (char *)g_list_nth_data(g->watermarks_filenames, n), sizeof(p->filename));
902 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
903}
904
906{
909
910 if(fabsf(p->color[0] - self->picked_color[0]) < 0.0001f
911 && fabsf(p->color[1] - self->picked_color[1]) < 0.0001f
912 && fabsf(p->color[2] - self->picked_color[2]) < 0.0001f)
913 {
914 // interrupt infinite loops
915 return;
916 }
917
918 GdkRGBA c = {.red = self->picked_color[0],
919 .green = self->picked_color[1],
920 .blue = self->picked_color[2],
921 .alpha = 1.0 };
922
923 p->color[0] = self->picked_color[0];
924 p->color[1] = self->picked_color[1];
925 p->color[2] = self->picked_color[2];
926 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(g->colorpick), &c);
927
928 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
929}
930
931static void load_watermarks(const char *basedir, dt_iop_watermark_gui_data_t *g)
932{
933 GList *files = NULL;
934 char *watermarks_dir = g_build_filename(basedir, "watermarks", NULL);
935 GDir *dir = g_dir_open(watermarks_dir, 0, NULL);
936 if(dir)
937 {
938 const gchar *d_name;
939 while((d_name = g_dir_read_name(dir)))
940 files = g_list_prepend(files, g_strdup(d_name));
941 g_dir_close(dir);
942 }
943
944 files = g_list_sort(files, (GCompareFunc)g_strcmp0);
945 for(GList *iter = files; iter; iter = g_list_next(iter))
946 {
947 char *filename = iter->data;
948 gchar *extension = strrchr(filename, '.');
949 if(extension)
950 {
951 // we add only supported file formats to the list
952 if(!g_ascii_strcasecmp(extension, ".svg") || !g_ascii_strcasecmp(extension, ".png"))
953 {
954 // remember the whole filename for later
955 g->watermarks_filenames = g_list_append(g->watermarks_filenames, g_strdup(filename));
956 // ... and build string shown in the gui
957 *extension = '\0';
958 extension++;
959 gchar *text = g_strdup_printf("%s (%s)", filename, extension);
960 dt_bauhaus_combobox_add(g->watermarks, text);
961 dt_free(text);
962 }
963 }
964 }
965
966 g_list_free_full(files, dt_free_gpointer);
967 files = NULL;
968 dt_free(watermarks_dir);
969}
970
972{
975
976 g_signal_handlers_block_by_func(g->watermarks, watermark_callback, self);
977
978 // Clear combobox...
979 dt_bauhaus_combobox_clear(g->watermarks);
980 g_list_free_full(g->watermarks_filenames, dt_free_gpointer);
981 g->watermarks_filenames = NULL;
982
983 // check watermarkdir and update combo with entries...
984 gchar configdir[DT_PATH_MAX] = { 0 };
985 gchar datadir[DT_PATH_MAX] = { 0 };
986 dt_loc_get_datadir(datadir, sizeof(datadir));
987 dt_loc_get_user_config_dir(configdir, sizeof(configdir));
988
989 load_watermarks(datadir, g);
990 load_watermarks(configdir, g);
991
992 _combo_box_set_active_text(g, p->filename);
993
994 g_signal_handlers_unblock_by_func(g->watermarks, watermark_callback, self);
995}
996
997static void refresh_callback(GtkWidget *tb, gpointer user_data)
998{
999 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
1000 refresh_watermarks(self);
1001}
1002
1003static void alignment_callback(GtkWidget *tb, gpointer user_data)
1004{
1005 int index = -1;
1006 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
1008
1009 if(dt_gui_widgets_suppressed()) return;
1011
1012
1013 for(int i = 0; i < 9; i++)
1014 {
1015 /* block signal handler */
1016 g_signal_handlers_block_by_func(g->align[i], alignment_callback, user_data);
1017
1018 if(GTK_WIDGET(g->align[i]) == tb)
1019 {
1020 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->align[i]), TRUE);
1021 index = i;
1022 }
1023 else
1024 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->align[i]), FALSE);
1025
1026 /* unblock signal handler */
1027 g_signal_handlers_unblock_by_func(g->align[i], alignment_callback, user_data);
1028 }
1029 p->alignment = index;
1030 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
1031}
1032
1033static void text_callback(GtkWidget *entry, gpointer user_data)
1034{
1035 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
1036 if(dt_gui_widgets_suppressed()) return;
1038 g_strlcpy(p->text, gtk_entry_get_text(GTK_ENTRY(entry)), sizeof(p->text));
1039 dt_conf_set_string("plugins/darkroom/watermark/text", p->text);
1040 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
1041}
1042
1043static void colorpick_color_set(GtkColorButton *widget, gpointer user_data)
1044{
1045 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
1046 if(dt_gui_widgets_suppressed()) return;
1048
1049 GdkRGBA c;
1050 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &c);
1051 p->color[0] = c.red;
1052 p->color[1] = c.green;
1053 p->color[2] = c.blue;
1054
1055 dt_conf_set_float("plugins/darkroom/watermark/color_red", p->color[0]);
1056 dt_conf_set_float("plugins/darkroom/watermark/color_green", p->color[1]);
1057 dt_conf_set_float("plugins/darkroom/watermark/color_blue", p->color[2]);
1058 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
1059}
1060
1061static void fontsel_callback(GtkWidget *button, gpointer user_data)
1062{
1063 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
1064 if(dt_gui_widgets_suppressed()) return;
1066
1067 gchar *fontname = gtk_font_chooser_get_font(GTK_FONT_CHOOSER(button));
1068 g_strlcpy(p->font, fontname, sizeof(p->font));
1069 dt_free(fontname);
1070 dt_conf_set_string("plugins/darkroom/watermark/font", p->font);
1071 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
1072}
1073
1076{
1079
1080 d->opacity = p->opacity;
1081 d->scale = p->scale;
1082 d->rotate = p->rotate;
1083 d->xoffset = p->xoffset;
1084 d->yoffset = p->yoffset;
1085 d->alignment = p->alignment;
1086 d->sizeto = p->sizeto;
1087 memset(d->filename, 0, sizeof(d->filename));
1088 g_strlcpy(d->filename, p->filename, sizeof(d->filename));
1089 memset(d->text, 0, sizeof(d->text));
1090 g_strlcpy(d->text, p->text, sizeof(d->text));
1091 for (int k=0; k<3; k++)
1092 d->color[k] = p->color[k];
1093 memset(d->font, 0, sizeof(d->font));
1094 g_strlcpy(d->font, p->font, sizeof(d->font));
1095
1096// fprintf(stderr, "Commit params: %s...\n",d->filename);
1097}
1098
1100{
1102 piece->data_size = sizeof(dt_iop_watermark_data_t);
1103}
1104
1106{
1107 dt_free_align(piece->data);
1108 piece->data = NULL;
1109}
1110
1111
1112void gui_update(struct dt_iop_module_t *self)
1113{
1116 for(int i = 0; i < 9; i++)
1117 {
1118 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->align[i]), FALSE);
1119 }
1120 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->align[p->alignment]), TRUE);
1121 _combo_box_set_active_text(g, p->filename);
1122 gtk_entry_set_text(GTK_ENTRY(g->text), p->text);
1123 GdkRGBA color = (GdkRGBA){.red = p->color[0], .green = p->color[1], .blue = p->color[2], .alpha = 1.0 };
1124 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(g->colorpick), &color);
1125 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(g->fontsel), p->font);
1126}
1127
1129{
1130 dt_iop_default_init(module);
1131}
1132
1134{
1135 dt_iop_watermark_params_t *d = module->default_params;
1136 g_strlcpy(d->filename, "simple-text.svg", sizeof(d->filename));
1137 g_strlcpy(d->font, "DejaVu Sans 10", sizeof(d->font));
1138}
1139
1140void gui_init(struct dt_iop_module_t *self)
1141{
1144
1145 self->gui->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1146
1147 GtkGrid *grid = GTK_GRID(gtk_grid_new());
1148 gtk_grid_set_row_spacing(grid, DT_GUI_BOX_SPACING);
1149 gtk_grid_set_column_spacing(grid, DT_GUI_BOX_SPACING);
1150 int line = 0;
1151
1152 // Add the marker combobox
1153 gchar configdir[DT_PATH_MAX] = { 0 };
1154 gchar datadir[DT_PATH_MAX] = { 0 };
1155 dt_loc_get_datadir(datadir, sizeof(datadir));
1156 dt_loc_get_user_config_dir(configdir, sizeof(configdir));
1157
1158 GtkWidget *label = dt_iop_gui_reset_label_new(_("marker"), self, &p->filename, sizeof(p->filename));
1160 gtk_widget_set_hexpand(GTK_WIDGET(g->watermarks), TRUE);
1161 char *tooltip = g_strdup_printf(_("SVG watermarks in %s/watermarks or %s/watermarks"), configdir, datadir);
1162 gtk_widget_set_tooltip_text(g->watermarks, tooltip);
1164 g->refresh = dtgtk_button_new(dtgtk_cairo_paint_refresh, 0, NULL);
1165
1166 gtk_grid_attach(grid, label, 0, line++, 1, 1);
1167 gtk_grid_attach_next_to(grid, g->watermarks, label, GTK_POS_RIGHT, 1, 1);
1168 gtk_grid_attach_next_to(grid, g->refresh, g->watermarks, GTK_POS_RIGHT, 1, 1);
1169
1170 // Simple text
1171 label = dt_ui_label_new(_("text"));
1172 g->text = gtk_entry_new();
1174 gtk_entry_set_width_chars(GTK_ENTRY(g->text), 1);
1175 gtk_widget_set_tooltip_text(g->text, _("text string, tag:\n$(WATERMARK_TEXT)"));
1176 const char *str = dt_conf_get_string_const("plugins/darkroom/watermark/text");
1177 gtk_entry_set_text(GTK_ENTRY(g->text), str);
1178
1179 gtk_grid_attach(grid, label, 0, line++, 1, 1);
1180 gtk_grid_attach_next_to(grid, g->text, label, GTK_POS_RIGHT, 2, 1);
1181
1182 // Text font
1183 label = dt_iop_gui_reset_label_new(_("font"), self, &p->font, sizeof(p->font));
1184 str = dt_conf_get_string_const("plugins/darkroom/watermark/font");
1185 g->fontsel = gtk_font_button_new_with_font(str==NULL?"DejaVu Sans 10":str);
1186 GtkWidget *child = dt_gui_container_first_child(GTK_CONTAINER(gtk_bin_get_child(GTK_BIN(g->fontsel))));
1187 gtk_label_set_ellipsize(GTK_LABEL(child), PANGO_ELLIPSIZE_MIDDLE);
1188 gtk_widget_set_tooltip_text(g->fontsel, _("text font, tags:\n$(WATERMARK_FONT_FAMILY)\n"
1189 "$(WATERMARK_FONT_STYLE)\n$(WATERMARK_FONT_WEIGHT)"));
1190 gtk_font_button_set_show_size (GTK_FONT_BUTTON(g->fontsel), FALSE);
1191
1192 gtk_grid_attach(grid, label, 0, line++, 1, 1);
1193 gtk_grid_attach_next_to(grid, g->fontsel, label, GTK_POS_RIGHT, 2, 1);
1194
1195 // Watermark color
1196 float red = dt_conf_get_float("plugins/darkroom/watermark/color_red");
1197 float green = dt_conf_get_float("plugins/darkroom/watermark/color_green");
1198 float blue = dt_conf_get_float("plugins/darkroom/watermark/color_blue");
1199 GdkRGBA color = (GdkRGBA){.red = red, .green = green, .blue = blue, .alpha = 1.0 };
1200
1201 label = dt_iop_gui_reset_label_new(_("color"), self, &p->color, 3 * sizeof(float));
1202 g->colorpick = gtk_color_button_new_with_rgba(&color);
1203 gtk_widget_set_tooltip_text(g->colorpick, _("watermark color, tag:\n$(WATERMARK_COLOR)"));
1204 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(g->colorpick), FALSE);
1205 gtk_color_button_set_title(GTK_COLOR_BUTTON(g->colorpick), _("select watermark color"));
1206 g->color_picker_button = dt_color_picker_new(self, DT_COLOR_PICKER_POINT, NULL);
1207 gtk_widget_set_tooltip_text(GTK_WIDGET(g->color_picker_button), _("pick color from image"));
1208
1209 gtk_grid_attach(grid, label, 0, line++, 1, 1);
1210 gtk_grid_attach_next_to(grid, g->colorpick, label, GTK_POS_RIGHT, 1, 1);
1211 gtk_grid_attach_next_to(grid, g->color_picker_button, g->colorpick, GTK_POS_RIGHT, 1, 1);
1212
1213 gtk_box_pack_start(GTK_BOX(self->gui->widget), GTK_WIDGET(grid), TRUE, TRUE, 0);
1214
1215 // Add opacity/scale sliders to table
1216 g->opacity = dt_bauhaus_slider_from_params(self, N_("opacity"));
1217 dt_bauhaus_slider_set_format(g->opacity, "%");
1218
1219 gtk_box_pack_start(GTK_BOX(self->gui->widget), dt_ui_section_label_new(_("placement")), TRUE, TRUE, 0);
1220
1221 // rotate
1222 g->rotate = dt_bauhaus_slider_from_params(self, "rotate");
1223 dt_bauhaus_slider_set_format(g->rotate, "\302\260");
1224
1225 // scale
1226 g->scale = dt_bauhaus_slider_from_params(self, N_("scale"));
1227 dt_bauhaus_slider_set_soft_max(g->scale, 100.0);
1228 dt_bauhaus_slider_set_format(g->scale, "%");
1229
1230 // scale-on
1231 g->sizeto = dt_bauhaus_combobox_from_params(self, "sizeto");
1232 gtk_widget_set_tooltip_text(g->sizeto, _("size is relative to"));
1233
1234 // Create the 3x3 gtk table toggle button table...
1235 GtkWidget *bat = gtk_grid_new();
1236 label = dt_iop_gui_reset_label_new(_("alignment"), self, &p->alignment, sizeof(p->alignment));
1237 gtk_grid_attach(GTK_GRID(bat), label, 0, 0, 1, 3);
1238 gtk_widget_set_hexpand(label, TRUE);
1239 gtk_grid_set_row_spacing(GTK_GRID(bat), DT_GUI_BOX_SPACING);
1240 gtk_grid_set_column_spacing(GTK_GRID(bat), DT_GUI_BOX_SPACING);
1241 for(int i = 0; i < 9; i++)
1242 {
1244 gtk_grid_attach(GTK_GRID(bat), GTK_WIDGET(g->align[i]), 1 + i%3, i/3, 1, 1);
1245 g_signal_connect(G_OBJECT(g->align[i]), "toggled", G_CALLBACK(alignment_callback), self);
1246 }
1247
1248 gtk_box_pack_start(GTK_BOX(self->gui->widget), bat, FALSE, FALSE, 0);
1249
1250 // x/y offset
1251 g->x_offset = dt_bauhaus_slider_from_params(self, "xoffset");
1252 dt_bauhaus_slider_set_digits(g->x_offset, 3);
1253 g->y_offset = dt_bauhaus_slider_from_params(self, "yoffset");
1254 dt_bauhaus_slider_set_digits(g->y_offset, 3);
1255
1256 // Let's add some tooltips and hook up some signals...
1257 gtk_widget_set_tooltip_text(g->opacity, _("the opacity of the watermark"));
1258 gtk_widget_set_tooltip_text(g->scale, _("the scale of the watermark"));
1259 gtk_widget_set_tooltip_text(g->rotate, _("the rotation of the watermark"));
1260
1261 refresh_watermarks(self);
1262
1263 g_signal_connect(G_OBJECT(g->watermarks), "value-changed", G_CALLBACK(watermark_callback), self);
1264 g_signal_connect(G_OBJECT(g->refresh), "clicked", G_CALLBACK(refresh_callback), self);
1265 g_signal_connect(G_OBJECT(g->text), "changed", G_CALLBACK(text_callback), self);
1266 g_signal_connect(G_OBJECT(g->colorpick), "color-set", G_CALLBACK(colorpick_color_set), self);
1267 g_signal_connect(G_OBJECT(g->fontsel), "font-set", G_CALLBACK(fontsel_callback), self);
1268}
1269
1271{
1273 g_list_free_full(g->watermarks_filenames, dt_free_gpointer);
1274 g->watermarks_filenames = NULL;
1275
1277}
1278
1279// clang-format off
1280// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1281// vim: shiftwidth=2 expandtab tabstop=2 cindent
1282// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1283// clang-format on
void dt_accels_disconnect_on_text_input(GtkWidget *widget)
Disconnect accels while a text or search entry has the focus, and reconnect them when it loses it....
Handle default and user-set shortcuts (accelerators)
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
const char * extension(dt_imageio_module_data_t *data)
Definition avif.c:651
void dt_bauhaus_slider_set_digits(GtkWidget *widget, int val)
Definition bauhaus.c:3343
void dt_bauhaus_combobox_clear(GtkWidget *widget)
Definition bauhaus.c:1997
int dt_bauhaus_combobox_get(GtkWidget *widget)
Definition bauhaus.c:2136
void dt_bauhaus_slider_set_soft_max(GtkWidget *widget, float val)
Definition bauhaus.c:1474
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2090
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
GtkWidget * dtgtk_button_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
Definition button.c:135
@ IOP_CS_RGB
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
void dt_conf_set_float(const char *name, float val)
float dt_conf_get_float(const char *name)
Float for name, clamped to its declared bounds.
void dt_conf_set_string(const char *name, const char *val)
const char * dt_conf_get_string_const(const char *name)
Borrow the stored string for name without copying it.
void dt_image_full_path(const int32_t imgid, char *pathname, size_t pathname_len, gboolean *from_cache, const char *calling_func)
Get the full path of an image out of the database.
GtkWidget * dt_gui_container_first_child(GtkContainer *container)
Definition container.c:43
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
void default_input_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:127
static int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Initialise a mutex. With mutexattr NULL – which is how 54 of the 56 call sites in this tree spell it ...
Definition dtpthread.h:104
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:117
void dt_loc_get_datadir(char *datadir, size_t bufsize)
void dt_loc_get_user_config_dir(char *configdir, size_t bufsize)
@ TYPE_FLOAT
Definition format.h:56
GdkRGBA color[]
Definition geotagging.c:541
#define DT_GUI_MODULE(x)
const char * tooltip
Definition image.h:310
static void dt_iop_image_copy_by_size(float *const __restrict__ out, const float *const __restrict__ in, const size_t width, const size_t height, const size_t ch)
Definition imagebuf.h:91
int dimension(struct dt_imageio_module_format_t *self, dt_imageio_module_data_t *data, uint32_t *width, uint32_t *height)
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_INCLUDE_IN_STYLES
Definition imageop.h:185
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:186
@ IOP_GROUP_EFFECTS
Definition imageop.h:161
@ IOP_TAG_DECORATION
Definition imageop.h:171
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)
#define IOP_GUI_FREE
Definition imageop_gui.h:96
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
GtkWidget * dt_ui_section_label_new(const gchar *str)
Definition label.c:114
GtkWidget * dt_ui_label_new(const gchar *str)
Definition label.c:125
_lib_location_type_t type
Definition location.c:1
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
#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 M_PI
Definition math.h:47
#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 dt_lib_export_metadata_get_conf_flags(void)
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
#define DT_PATH_MAX
Buffer size for a filesystem path anywhere in Ansel.
Definition paths.h:57
char * dt_variables_expand(dt_variables_params_t *params, gchar *source, gboolean iterate)
void dt_variables_params_destroy(dt_variables_params_t *params)
void dt_variables_params_init(dt_variables_params_t **params)
void dt_variables_set_tags_flags(dt_variables_params_t *params, uint32_t flags)
struct dt_iop_module_t *void * data
struct dt_develop_t * dev
dt_image_t image_storage
Definition develop.h:225
int32_t id
Definition image.h:401
unsigned int channels
Definition format.h:83
dt_iop_buffer_type_t datatype
Definition format.h:85
GtkWidget * widget
Definition imageop_gui.h:47
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_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
dt_iop_watermark_base_scale_t sizeto
Definition watermark.c:163
GtkWidget * color_picker_button
Definition watermark.c:182
dt_iop_watermark_base_scale_t sizeto
Definition watermark.c:145
#define __DT_CLONE_TARGETS__
GtkWidget * dtgtk_togglebutton_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
gchar * dt_util_str_replace(const gchar *string, const gchar *pattern, const gchar *substitute)
Definition utility.c:140
RsvgDimensionData dt_get_svg_dimension(RsvgHandle *svg)
Definition utility.c:961
void dt_render_svg(RsvgHandle *svg, cairo_t *cr, double width, double height, double offset_x, double offset_y)
Definition utility.c:994
int operation_tags()
Definition watermark.c:375
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 watermark.c:1074
void init(dt_iop_module_t *module)
Definition watermark.c:1128
static void colorpick_color_set(GtkColorButton *widget, gpointer user_data)
Definition watermark.c:1043
const char ** description(struct dt_iop_module_t *self)
Definition watermark.c:356
int default_group()
Definition watermark.c:370
__DT_CLONE_TARGETS__ 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 watermark.c:518
static void watermark_callback(GtkWidget *tb, gpointer user_data)
Definition watermark.c:891
void reload_defaults(dt_iop_module_t *module)
Definition watermark.c:1133
static void _text_color_font_set_sensitive(dt_iop_watermark_gui_data_t *g, gchar *filename)
Definition watermark.c:394
static void fontsel_callback(GtkWidget *button, gpointer user_data)
Definition watermark.c:1061
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition watermark.c:1099
static gchar * _string_escape(const gchar *string)
Definition watermark.c:423
dt_iop_watermark_base_scale_t
Definition watermark.c:119
@ DT_SCALE_SMALLER_BORDER
Definition watermark.c:122
@ DT_SCALE_IMAGE
Definition watermark.c:120
@ DT_SCALE_LARGER_BORDER
Definition watermark.c:121
const char * name()
Definition watermark.c:351
static void refresh_watermarks(dt_iop_module_t *self)
Definition watermark.c:971
void gui_update(struct dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
Definition watermark.c:1112
static gchar * _string_substitute(gchar *string, const gchar *search, const gchar *replace)
Definition watermark.c:439
static void refresh_callback(GtkWidget *tb, gpointer user_data)
Definition watermark.c:997
void gui_init(struct dt_iop_module_t *self)
Definition watermark.c:1140
static void text_callback(GtkWidget *entry, gpointer user_data)
Definition watermark.c:1033
static void alignment_callback(GtkWidget *tb, gpointer user_data)
Definition watermark.c:1003
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition watermark.c:380
void input_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
Definition watermark.c:385
int flags()
Definition watermark.c:365
void gui_cleanup(struct dt_iop_module_t *self)
Definition watermark.c:1270
static pthread_once_t _rsvg_lock_once
Definition watermark.c:107
dt_iop_watermark_type_t
Definition watermark.c:126
@ DT_WTM_PNG
Definition watermark.c:128
@ DT_WTM_SVG
Definition watermark.c:127
static void load_watermarks(const char *basedir, dt_iop_watermark_gui_data_t *g)
Definition watermark.c:931
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition watermark.c:1105
static void _combo_box_set_active_text(dt_iop_watermark_gui_data_t *g, gchar *text)
Definition watermark.c:407
static void _rsvg_lock_init(void)
Definition watermark.c:109
void color_picker_apply(dt_iop_module_t *self, GtkWidget *picker, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition watermark.c:905
static dt_pthread_mutex_t _rsvg_lock
Definition watermark.c:106
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 watermark.c:185
static gchar * _watermark_get_svgdoc(dt_iop_module_t *self, dt_iop_watermark_data_t *data, const dt_image_t *image, const gchar *filename)
Definition watermark.c:448
gboolean dt_gui_widgets_suppressed(void)
#define DT_GUI_BOX_SPACING
void dtgtk_cairo_paint_refresh(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_alignment(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
@ CPF_SPECIAL_FLAG