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
59#include "common/darktable.h"
60#include "bauhaus/bauhaus.h"
61#include "common/imagebuf.h"
62#include "common/tags.h"
63#include "common/variables.h"
64#include "common/datetime.h"
65#include "control/control.h"
66#include "develop/develop.h"
67#include "develop/imageop.h"
68#include "develop/imageop_gui.h"
69#include "dtgtk/button.h"
70#include "dtgtk/resetlabel.h"
71#include "dtgtk/togglebutton.h"
72
74#include "gui/gtk.h"
75#include "iop/iop_api.h"
76#include <assert.h>
77#include <gtk/gtk.h>
78#include <inttypes.h>
79#include <math.h>
80#include <stdlib.h>
81#include <string.h>
82
83#include <librsvg/rsvg.h>
84// ugh, ugly hack. why do people break stuff all the time?
85#ifndef RSVG_CAIRO_H
86#include <librsvg/rsvg-cairo.h>
87#endif
88
90#include "common/metadata.h"
91#include "common/utility.h"
92
94
95// gchar *checksum = g_compute_checksum_for_data(G_CHECKSUM_MD5,data,length);
96
98{
99 DT_SCALE_IMAGE = 0, // $DESCRIPTION: "image"
100 DT_SCALE_LARGER_BORDER = 1, // $DESCRIPTION: "larger border"
101 DT_SCALE_SMALLER_BORDER = 2 // $DESCRIPTION: "smaller border"
103
105{
106 DT_WTM_SVG = 0, // $DESCRIPTION: "vector .svg"
107 DT_WTM_PNG = 1 // $DESCRIPTION: "raster .png"
109
111{
113 float opacity; // $MIN: 0.0 $MAX: 100.0 $DEFAULT: 100.0
115 float scale; // $MIN: 1.0 $MAX: 500.0 $DEFAULT: 100.0
117 float xoffset; // $MIN: -1.0 $MAX: 1.0, 0.001 $DEFAULT: 0.0 $DESCRIPTION: "x offset"
119 float yoffset; // $MIN: -1.0 $MAX: 1.0, 0.001 $DEFAULT: 0.0 $DESCRIPTION: "y offset"
121 int alignment; // $DEFAULT: 4
123 float rotate; // $MIN: -180.0 $MAX: 180.0 $DEFAULT: 0.0 $DESCRIPTION: "rotation"
124 dt_iop_watermark_base_scale_t sizeto; // $DEFAULT: DT_SCALE_IMAGE $DESCRIPTION: "scale on"
125 char filename[64];
126 /* simple text */
127 char text[512];
128 /* text color */
129 float color[3]; // $DEFAULT: 0.0
130 /* text font */
131 char font[64];
133
148
150{
151 GtkWidget *watermarks; // watermark
152 GList *watermarks_filenames; // the actual filenames
153 GtkWidget *refresh; // refresh watermarks...
154 GtkWidget *align[9]; // Alignment buttons
155 GtkWidget *opacity, *scale, *x_offset, *y_offset; // opacity, scale, xoffs, yoffs
156 GtkWidget *sizeto; // relative size to
163
164int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
165 void *new_params, const int new_version)
166{
167 if(old_version == 1 && new_version == 5)
168 {
169 typedef struct dt_iop_watermark_params_v1_t
170 {
172 float opacity;
174 float scale;
176 float xoffset;
178 float yoffset;
180 int alignment;
181 char filename[64];
182 } dt_iop_watermark_params_v1_t;
183
184 dt_iop_watermark_params_v1_t *o = (dt_iop_watermark_params_v1_t *)old_params;
187
188 *n = *d; // start with a fresh copy of default parameters
189
190 n->opacity = o->opacity;
191 n->scale = o->scale;
192 n->xoffset = o->xoffset;
193 n->yoffset = o->yoffset;
194 n->alignment = o->alignment;
195 n->rotate = 0.0;
196 n->sizeto = DT_SCALE_IMAGE;
197 g_strlcpy(n->filename, o->filename, sizeof(n->filename));
198 g_strlcpy(n->text, "", sizeof(n->text));
199 g_strlcpy(n->font, "DejaVu Sans 10", sizeof(n->font));
200 n->color[0] = n->color[1] = n->color[2] = 0;
201 return 0;
202 }
203 else if(old_version == 2 && new_version == 5)
204 {
205 typedef struct dt_iop_watermark_params_v2_t
206 {
208 float opacity;
210 float scale;
212 float xoffset;
214 float yoffset;
216 int alignment;
218 char filename[64];
219 } dt_iop_watermark_params_v2_t;
220
221 dt_iop_watermark_params_v2_t *o = (dt_iop_watermark_params_v2_t *)old_params;
224
225 *n = *d; // start with a fresh copy of default parameters
226
227 n->opacity = o->opacity;
228 n->scale = o->scale;
229 n->xoffset = o->xoffset;
230 n->yoffset = o->yoffset;
231 n->alignment = o->alignment;
232 n->rotate = 0.0;
233 n->sizeto = DT_SCALE_IMAGE;
234 g_strlcpy(n->filename, o->filename, sizeof(n->filename));
235 g_strlcpy(n->text, "", sizeof(n->text));
236 g_strlcpy(n->font, "DejaVu Sans 10", sizeof(n->font));
237 n->color[0] = n->color[1] = n->color[2] = 0;
238 return 0;
239 }
240 else if(old_version == 3 && new_version == 5)
241 {
242 typedef struct dt_iop_watermark_params_v3_t
243 {
245 float opacity;
247 float scale;
249 float xoffset;
251 float yoffset;
253 int alignment;
255 float rotate;
257 char filename[64];
258 } dt_iop_watermark_params_v3_t;
259
260 dt_iop_watermark_params_v3_t *o = (dt_iop_watermark_params_v3_t *)old_params;
263
264 *n = *d; // start with a fresh copy of default parameters
265
266 n->opacity = o->opacity;
267 n->scale = o->scale;
268 n->xoffset = o->xoffset;
269 n->yoffset = o->yoffset;
270 n->alignment = o->alignment;
271 n->rotate = o->rotate;
272 n->sizeto = o->sizeto;
273 g_strlcpy(n->filename, o->filename, sizeof(n->filename));
274 g_strlcpy(n->text, "", sizeof(n->text));
275 g_strlcpy(n->font, "DejaVu Sans 10", sizeof(n->font));
276 n->color[0] = n->color[1] = n->color[2] = 0;
277 return 0;
278 }
279 else if(old_version == 4 && new_version == 5)
280 {
281 typedef struct dt_iop_watermark_params_v4_t
282 {
284 float opacity;
286 float scale;
288 float xoffset;
290 float yoffset;
292 int alignment;
294 float rotate;
296 char filename[64];
297 /* simple text */
298 char text[64];
299 /* text color */
300 float color[3];
301 /* text font */
302 char font[64];
303 } dt_iop_watermark_params_v4_t;
304
305 dt_iop_watermark_params_v4_t *o = (dt_iop_watermark_params_v4_t *)old_params;
308
309 *n = *d; // start with a fresh copy of default parameters
310
311 n->opacity = o->opacity;
312 n->scale = o->scale;
313 n->xoffset = o->xoffset;
314 n->yoffset = o->yoffset;
315 n->alignment = o->alignment;
316 n->rotate = o->rotate;
317 n->sizeto = o->sizeto;
318 g_strlcpy(n->filename, o->filename, sizeof(n->filename));
319 g_strlcpy(n->text, o->text, sizeof(n->text));
320 g_strlcpy(n->font, o->font, sizeof(n->font));
321 n->color[0] = o->color[0];
322 n->color[1] = o->color[1];
323 n->color[2] = o->color[2];
324 return 0;
325 }
326 return 1;
327}
328
329
330const char *name()
331{
332 return _("_Watermark");
333}
334
335const char **description(struct dt_iop_module_t *self)
336{
337 return dt_iop_set_description(self, _("overlay an SVG watermark like a signature on the picture"),
338 _("creative"),
339 _("non-linear, RGB, display-referred"),
340 _("non-linear, RGB"),
341 _("non-linear, RGB, display-referred"));
342}
343
348
350{
351 return IOP_GROUP_EFFECTS;
352}
353
355{
356 return IOP_TAG_DECORATION;
357}
358
360{
361 return IOP_CS_RGB;
362}
363
366{
367 default_input_format(self, pipe, piece, dsc);
368 dsc->channels = 4;
369 dsc->datatype = TYPE_FLOAT;
370}
371
372// sets text / color / font widgets sensitive based on watermark file type
374{
375 const gchar *extension = strrchr(filename, '.');
377 {
378 const gboolean active = !g_ascii_strcasecmp(extension, ".svg");
379 gtk_widget_set_sensitive(GTK_WIDGET(g->colorpick), active);
380 gtk_widget_set_sensitive(GTK_WIDGET(g->color_picker_button), active);
381 gtk_widget_set_sensitive(GTK_WIDGET(g->text), active);
382 gtk_widget_set_sensitive(GTK_WIDGET(g->fontsel), active);
383 }
384}
385
387{
388 int i = 0;
389 for(const GList *iter = g->watermarks_filenames; iter; iter = g_list_next(iter))
390 {
391 if(!g_strcmp0((gchar *)iter->data, text))
392 {
393 dt_bauhaus_combobox_set(g->watermarks, i);
395 return;
396 }
397 i++;
398 }
399}
400
401// replace < and > with &lt; and &gt;. any more? Yes! & -> &amp;
402static gchar *_string_escape(const gchar *string)
403{
404 gchar *result, *result_old;
405 result = dt_util_str_replace(string, "&", "&amp;");
406
407 result_old = result;
408 result = dt_util_str_replace(result_old, "<", "&lt;");
409 dt_free(result_old);
410
411 result_old = result;
412 result = dt_util_str_replace(result_old, ">", "&gt;");
413 dt_free(result_old);
414
415 return result;
416}
417
418static gchar *_string_substitute(gchar *string, const gchar *search, const gchar *replace)
419{
420 gchar *_replace = _string_escape(replace);
421 gchar *result = dt_util_str_replace(string, search, _replace);
422 dt_free(_replace);
423 dt_free(string); // dt_util_str_replace always returns a new string, and we don't need the original after this func
424 return result;
425}
426
428 const dt_image_t *image, const gchar *filename)
429{
430 gchar *svgdata = NULL;
431 gsize length = 0;
432 if(g_file_get_contents(filename, &svgdata, &length, NULL))
433 {
434 // File is loaded lets substitute strings if found...
435 // Simple text from watermark module
436 gchar buffer[1024];
437
438 // substitute $(WATERMARK_TEXT)
439 if(data->text[0])
440 {
441 g_strlcpy(buffer, data->text, sizeof(buffer));
442 svgdata = _string_substitute(svgdata, "$(WATERMARK_TEXT)", buffer);
443 }
444 // apply font style substitutions
445 PangoFontDescription *font = pango_font_description_from_string(data->font);
446 const PangoStyle font_style = pango_font_description_get_style(font);
447 const int font_weight = (int)pango_font_description_get_weight(font);
448
449 g_strlcpy(buffer, pango_font_description_get_family(font), sizeof(buffer));
450 svgdata = _string_substitute(svgdata, "$(WATERMARK_FONT_FAMILY)", buffer);
451
452 switch(font_style)
453 {
454 case PANGO_STYLE_OBLIQUE:
455 g_strlcpy(buffer, "oblique", sizeof(buffer));
456 break;
457 case PANGO_STYLE_ITALIC:
458 g_strlcpy(buffer, "italic", sizeof(buffer));
459 break;
460 default:
461 g_strlcpy(buffer, "normal", sizeof(buffer));
462 break;
463 }
464 svgdata = _string_substitute(svgdata, "$(WATERMARK_FONT_STYLE)", buffer);
465
466 g_snprintf(buffer, sizeof(buffer), "%d", font_weight);
467 svgdata = _string_substitute(svgdata, "$(WATERMARK_FONT_WEIGHT)", buffer);
468
469 pango_font_description_free(font);
470
471 // watermark color
472 GdkRGBA c = { data->color[0], data->color[1], data->color[2], 1.0f };
473 g_strlcpy(buffer, gdk_rgba_to_string(&c), sizeof(buffer));
474 svgdata = _string_substitute(svgdata, "$(WATERMARK_COLOR)", buffer);
475
476 // standard calculation on the remaining variables
478 dt_variables_params_t *params;
480 gboolean from_cache = FALSE;
481 char image_path[PATH_MAX] = { 0 };
482 dt_image_full_path(image->id, image_path, sizeof(image_path), &from_cache, __FUNCTION__);
483 params->filename = image_path;
484 params->jobcode = "infos";
485 params->sequence = 0;
486 params->imgid = image->id;
488 gchar *svgdoc = dt_variables_expand(params, svgdata, FALSE); // returns a new string
490 dt_free(svgdata); // free the old one
491 svgdata = svgdoc; // and make the expanded string our result
492 }
493 return svgdata;
494}
495
497int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
498 void *const ovoid)
499{
500 const dt_iop_roi_t *const roi_in = &piece->roi_in;
501 const dt_iop_roi_t *const roi_out = &piece->roi_out;
503 float *in = (float *)ivoid;
504 float *out = (float *)ovoid;
505 const int ch = 4;
506 const float angle = (M_PI / 180) * (-data->rotate);
507
508 gchar configdir[PATH_MAX] = { 0 };
509 gchar datadir[PATH_MAX] = { 0 };
510 gchar *filename;
511 dt_loc_get_datadir(datadir, sizeof(datadir));
512 dt_loc_get_user_config_dir(configdir, sizeof(configdir));
513 g_strlcat(datadir, "/watermarks/", sizeof(datadir));
514 g_strlcat(configdir, "/watermarks/", sizeof(configdir));
515 g_strlcat(datadir, data->filename, sizeof(datadir));
516 g_strlcat(configdir, data->filename, sizeof(configdir));
517
518 if(g_file_test(configdir, G_FILE_TEST_EXISTS))
519 filename = configdir;
520 else if(g_file_test(datadir, G_FILE_TEST_EXISTS))
521 filename = datadir;
522 else
523 {
524 dt_iop_image_copy_by_size(ovoid, ivoid, roi_out->width, roi_out->height, ch);
525 return 0;
526 }
527
528 // find out the watermark type
530 const gchar *extension = strrchr(data->filename, '.');
532 {
533 if(!g_ascii_strcasecmp(extension, ".svg"))
535 else if(!g_ascii_strcasecmp(extension, ".png"))
537 else // this should not happen
538 {
539 dt_iop_image_copy_by_size(ovoid, ivoid, roi_out->width, roi_out->height, ch);
540 return 0;
541 }
542 }
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 /* Load svg if not loaded */
550 gchar *svgdoc = NULL;
551 if(type == DT_WTM_SVG)
552 {
553 svgdoc = _watermark_get_svgdoc(self, data, &pipe->dev->image_storage, filename);
554 if(IS_NULL_PTR(svgdoc))
555 {
556 dt_iop_image_copy_by_size(ovoid, ivoid, roi_out->width, roi_out->height, ch);
557 return 0;
558 }
559 }
560
561 /* setup stride for performance */
562 const int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, roi_out->width);
563 if(stride == -1)
564 {
565 fprintf(stderr, "[watermark] cairo stride error\n");
566 dt_iop_image_copy_by_size(ovoid, ivoid, roi_out->width, roi_out->height, ch);
567 return 0;
568 }
569
570 /* create a cairo memory surface that is later used for reading watermark overlay data */
571 guint8 *image = (guint8 *)g_try_malloc0_n(roi_out->height, stride);
572 if(IS_NULL_PTR(image))
573 {
574 dt_iop_image_copy_by_size(ovoid, ivoid, roi_out->width, roi_out->height, ch);
575 return 1;
576 }
577 cairo_surface_t *surface = cairo_image_surface_create_for_data(image, CAIRO_FORMAT_ARGB32, roi_out->width,
578 roi_out->height, stride);
579 if(cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS)
580 {
581 fprintf(stderr, "[watermark] cairo surface error: %s\n",
582 cairo_status_to_string(cairo_surface_status(surface)));
583 dt_free(image);
584 dt_iop_image_copy_by_size(ovoid, ivoid, roi_out->width, roi_out->height, ch);
585 return 0;
586 }
587
588 // rsvg (or some part of cairo which is used underneath) isn't thread safe, for example when handling fonts
590
591 RsvgHandle *svg = NULL;
592 if(type == DT_WTM_SVG)
593 {
594 /* create the rsvghandle from parsed svg data */
595 GError *error = NULL;
596 svg = rsvg_handle_new_from_data((const guint8 *)svgdoc, strlen(svgdoc), &error);
597 dt_free(svgdoc);
598 if(IS_NULL_PTR(svg) || error)
599 {
600 cairo_surface_destroy(surface);
601 dt_free(image);
602 dt_iop_image_copy_by_size(ovoid, ivoid, roi_out->width, roi_out->height, ch);
604 fprintf(stderr, "[watermark] error processing svg file: %s\n", error->message);
605 g_error_free(error);
606 return 0;
607 }
608 }
609
610 // we use a second surface
611 guint8 *image_two = NULL;
612 cairo_surface_t *surface_two = NULL;
613
614 /* get the dimension of svg or png */
615 RsvgDimensionData dimension;
616 switch(type)
617 {
618 case DT_WTM_SVG:
620 break;
621 case DT_WTM_PNG:
622 // load png into surface 2
623 surface_two = cairo_image_surface_create_from_png(filename);
624 if((cairo_surface_status(surface_two) != CAIRO_STATUS_SUCCESS))
625 {
626 fprintf(stderr, "[watermark] cairo png surface 2 error: %s\n",
627 cairo_status_to_string(cairo_surface_status(surface_two)));
628 cairo_surface_destroy(surface);
629 dt_free(image);
630 dt_iop_image_copy_by_size(ovoid, ivoid, roi_out->width, roi_out->height, ch);
632 return 0;
633 }
634 dimension.width = cairo_image_surface_get_width(surface_two);
635 dimension.height = cairo_image_surface_get_height(surface_two);
636 break;
637 }
638
639 // if no text is given dimensions are null
640 if(!dimension.width) dimension.width = 1;
641 if(!dimension.height) dimension.height = 1;
642
643 // width/height of current (possibly cropped) image
644 const float iw = piece->buf_in.width;
645 const float ih = piece->buf_in.height;
646 const float uscale = data->scale / 100.0f; // user scale, from GUI in percent
647
648 // wbase, hbase are the base width and height, this is the multiplicator used for the offset computing
649 // scale is the scale of the watermark itself and is used only to render it.
650
651 float wbase, hbase, scale;
652
653 if(data->sizeto == DT_SCALE_IMAGE)
654 {
655 // in image mode, the wbase and hbase are just the image width and height
656 wbase = iw;
657 hbase = ih;
658 if(dimension.width > dimension.height)
659 scale = (iw * roi_out->scale) / dimension.width;
660 else
661 scale = (ih * roi_out->scale) / dimension.height;
662 }
663 else
664 {
665 // in larger/smaller side mode, set wbase and hbase to the largest or smallest side of the image
666 const float larger = dimension.width > dimension.height
667 ? (float)dimension.width
668 : (float)dimension.height;
669
670 if(iw > ih)
671 {
672 wbase = hbase = (data->sizeto == DT_SCALE_LARGER_BORDER) ? iw : ih;
673 scale = (data->sizeto == DT_SCALE_LARGER_BORDER) ? (iw / larger) : (ih / larger);
674 }
675 else
676 {
677 wbase = hbase = (data->sizeto == DT_SCALE_SMALLER_BORDER) ? iw : ih;
678 scale = (data->sizeto == DT_SCALE_SMALLER_BORDER) ? (iw / larger) : (ih / larger);
679 }
680 scale *= roi_out->scale;
681 }
682
683 scale *= uscale;
684
685 // compute the width and height of the SVG object in image dimension. This is only used to properly
686 // layout the watermark based on the alignment.
687
688 float svg_width, svg_height;
689
690 if(dimension.width > dimension.height)
691 {
692 if(data->sizeto == DT_SCALE_IMAGE || (iw > ih && data->sizeto == DT_SCALE_LARGER_BORDER)
693 || (iw < ih && data->sizeto == DT_SCALE_SMALLER_BORDER))
694 {
695 svg_width = iw * uscale;
696 svg_height = dimension.height * (svg_width / dimension.width);
697 }
698 else
699 {
700 svg_width = ih * uscale;
701 svg_height = dimension.height * (svg_width / dimension.width);
702 }
703 }
704 else
705 {
706 if(data->sizeto == DT_SCALE_IMAGE || (ih > iw && data->sizeto == DT_SCALE_LARGER_BORDER)
707 || (ih < iw && data->sizeto == DT_SCALE_SMALLER_BORDER))
708 {
709 svg_height = ih * uscale;
710 svg_width = dimension.width * (svg_height / dimension.height);
711 }
712 else
713 {
714 svg_height = iw * uscale;
715 svg_width = dimension.width * (svg_height / dimension.height);
716 }
717 }
718
719 /* For the rotation we need an extra cairo image as rotations are buggy via rsvg_handle_render_cairo.
720 distortions and blurred images are obvious but you also can easily have crashes.
721 */
722
723 float svg_offset_x = 0;
724 float svg_offset_y = 0;
725 if(type == DT_WTM_SVG)
726 {
727 /* the svg_offsets allow safe text boxes as they might render out of the dimensions */
728 svg_offset_x = ceilf(3.0f * scale);
729 svg_offset_y = ceilf(3.0f * scale);
730
731 const int watermark_width = (int)((dimension.width * scale) + 3* svg_offset_x);
732 const int watermark_height = (int)((dimension.height * scale) + 3* svg_offset_y) ;
733
734 const int stride_two = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, watermark_width);
735 image_two = (guint8 *)g_try_malloc0_n(watermark_height, stride_two);
736 if(IS_NULL_PTR(image_two))
737 {
738 cairo_surface_destroy(surface);
739 g_object_unref(svg);
740 dt_free(image);
741 dt_iop_image_copy_by_size(ovoid, ivoid, roi_out->width, roi_out->height, ch);
743 return 1;
744 }
745 surface_two = cairo_image_surface_create_for_data(image_two, CAIRO_FORMAT_ARGB32, watermark_width,
746 watermark_height, stride_two);
747 if(cairo_surface_status(surface_two) != CAIRO_STATUS_SUCCESS)
748 {
749 fprintf(stderr, "[watermark] cairo surface 2 error: %s\n",
750 cairo_status_to_string(cairo_surface_status(surface_two)));
751 cairo_surface_destroy(surface);
752 g_object_unref(svg);
753 dt_free(image);
754 dt_free(image_two);
755 dt_iop_image_copy_by_size(ovoid, ivoid, roi_out->width, roi_out->height, ch);
757 return 0;
758 }
759 }
760
761 /* create cairo context and setup transformation/scale */
762 cairo_t *cr = cairo_create(surface);
763 /* create cairo context for the scaled watermark */
764 cairo_t *cr_two = cairo_create(surface_two);
765
766 // compute bounding box of rotated watermark
767 const float bb_width = fabsf(svg_width * cosf(angle)) + fabsf(svg_height * sinf(angle));
768 const float bb_height = fabsf(svg_width * sinf(angle)) + fabsf(svg_height * cosf(angle));
769 const float bX = bb_width / 2.0f - svg_width / 2.0f;
770 const float bY = bb_height / 2.0f - svg_height / 2.0f;
771
772 // compute translation for the given alignment in image dimension
773
774 float ty = 0, tx = 0;
775 if(data->alignment >= 0 && data->alignment < 3) // Align to verttop
776 ty = bY;
777 else if(data->alignment >= 3 && data->alignment < 6) // Align to vertcenter
778 ty = (ih / 2.0f) - (svg_height / 2.0f);
779 else if(data->alignment >= 6 && data->alignment < 9) // Align to vertbottom
780 ty = ih - svg_height - bY;
781
782 if(data->alignment == 0 || data->alignment == 3 || data->alignment == 6)
783 tx = bX;
784 else if(data->alignment == 1 || data->alignment == 4 || data->alignment == 7)
785 tx = (iw / 2.0f) - (svg_width / 2.0f);
786 else if(data->alignment == 2 || data->alignment == 5 || data->alignment == 8)
787 tx = iw - svg_width - bX;
788
789 // translate to position
790 cairo_translate(cr, -roi_in->x, -roi_in->y);
791
792 // add translation for the given value in GUI (xoffset,yoffset)
793 tx += data->xoffset * wbase;
794 ty += data->yoffset * hbase;
795
796 cairo_translate(cr, tx * roi_out->scale, ty * roi_out->scale);
797
798 // compute the center of the svg to rotate from the center
799 const float cX = svg_width / 2.0f * roi_out->scale;
800 const float cY = svg_height / 2.0f * roi_out->scale;
801
802 cairo_translate(cr, cX, cY);
803 cairo_rotate(cr, angle);
804 cairo_translate(cr, -cX, -cY);
805
806 // now set proper scale and translationfor the watermark itself
807 cairo_translate(cr_two, svg_offset_x, svg_offset_y);
808
809 switch(type)
810 {
811 case DT_WTM_SVG:
812 cairo_scale(cr_two, scale, scale);
813 /* render svg into surface*/
814 dt_render_svg(svg, cr_two, dimension.width, dimension.height, 0, 0);
815 break;
816 case DT_WTM_PNG:
817 cairo_scale(cr, scale, scale);
818 break;
819 }
820 cairo_surface_flush(surface_two);
821
822 // paint the watermark
823 cairo_set_source_surface(cr, surface_two, -svg_offset_x, -svg_offset_y);
824 cairo_paint(cr);
825
826 // no more non-thread safe rsvg usage
828
829 cairo_destroy(cr);
830 cairo_destroy(cr_two);
831
832 /* ensure that all operations on surface finishing up */
833 cairo_surface_flush(surface);
834
835 /* render surface on output */
836 guint8 *sd = image;
837 const float opacity = data->opacity / 100.0f;
839 for(int j = 0; j < roi_out->height * roi_out->width; j++)
840 {
841 float *const i = in + ch*j;
842 float *const o = out + ch*j;
843 guint8 *const s = sd + 4*j;
844 const float alpha = (s[3] / 255.0f) * opacity;
845 /* svg uses a premultiplied alpha, so only use opacity for the blending */
846 o[0] = ((1.0f - alpha) * i[0]) + (opacity * (s[2] / 255.0f));
847 o[1] = ((1.0f - alpha) * i[1]) + (opacity * (s[1] / 255.0f));
848 o[2] = ((1.0f - alpha) * i[2]) + (opacity * (s[0] / 255.0f));
849 o[3] = in[3];
850 }
851
852 /* clean up */
853 cairo_surface_destroy(surface);
854 cairo_surface_destroy(surface_two);
855 dt_free(image);
856 if(type == DT_WTM_SVG)
857 {
858 dt_free(image_two);
859 g_object_unref(svg);
860 }
861
862 return 0;
863}
864
865static void watermark_callback(GtkWidget *tb, gpointer user_data)
866{
867 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
869
870 if(darktable.gui->reset) return;
872 memset(p->filename, 0, sizeof(p->filename));
873 int n = dt_bauhaus_combobox_get(g->watermarks);
874 g_strlcpy(p->filename, (char *)g_list_nth_data(g->watermarks_filenames, n), sizeof(p->filename));
877}
878
880{
883
884 if(fabsf(p->color[0] - self->picked_color[0]) < 0.0001f
885 && fabsf(p->color[1] - self->picked_color[1]) < 0.0001f
886 && fabsf(p->color[2] - self->picked_color[2]) < 0.0001f)
887 {
888 // interrupt infinite loops
889 return;
890 }
891
892 GdkRGBA c = {.red = self->picked_color[0],
893 .green = self->picked_color[1],
894 .blue = self->picked_color[2],
895 .alpha = 1.0 };
896
897 p->color[0] = self->picked_color[0];
898 p->color[1] = self->picked_color[1];
899 p->color[2] = self->picked_color[2];
900 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(g->colorpick), &c);
901
903}
904
905static void load_watermarks(const char *basedir, dt_iop_watermark_gui_data_t *g)
906{
907 GList *files = NULL;
908 char *watermarks_dir = g_build_filename(basedir, "watermarks", NULL);
909 GDir *dir = g_dir_open(watermarks_dir, 0, NULL);
910 if(dir)
911 {
912 const gchar *d_name;
913 while((d_name = g_dir_read_name(dir)))
914 files = g_list_prepend(files, g_strdup(d_name));
915 g_dir_close(dir);
916 }
917
918 files = g_list_sort(files, (GCompareFunc)g_strcmp0);
919 for(GList *iter = files; iter; iter = g_list_next(iter))
920 {
921 char *filename = iter->data;
922 gchar *extension = strrchr(filename, '.');
923 if(extension)
924 {
925 // we add only supported file formats to the list
926 if(!g_ascii_strcasecmp(extension, ".svg") || !g_ascii_strcasecmp(extension, ".png"))
927 {
928 // remember the whole filename for later
929 g->watermarks_filenames = g_list_append(g->watermarks_filenames, g_strdup(filename));
930 // ... and build string shown in the gui
931 *extension = '\0';
932 extension++;
933 gchar *text = g_strdup_printf("%s (%s)", filename, extension);
934 dt_bauhaus_combobox_add(g->watermarks, text);
935 dt_free(text);
936 }
937 }
938 }
939
940 g_list_free_full(files, dt_free_gpointer);
941 files = NULL;
942 dt_free(watermarks_dir);
943}
944
946{
949
950 g_signal_handlers_block_by_func(g->watermarks, watermark_callback, self);
951
952 // Clear combobox...
953 dt_bauhaus_combobox_clear(g->watermarks);
954 g_list_free_full(g->watermarks_filenames, dt_free_gpointer);
955 g->watermarks_filenames = NULL;
956
957 // check watermarkdir and update combo with entries...
958 gchar configdir[PATH_MAX] = { 0 };
959 gchar datadir[PATH_MAX] = { 0 };
960 dt_loc_get_datadir(datadir, sizeof(datadir));
961 dt_loc_get_user_config_dir(configdir, sizeof(configdir));
962
963 load_watermarks(datadir, g);
964 load_watermarks(configdir, g);
965
966 _combo_box_set_active_text(g, p->filename);
967
968 g_signal_handlers_unblock_by_func(g->watermarks, watermark_callback, self);
969}
970
971static void refresh_callback(GtkWidget *tb, gpointer user_data)
972{
973 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
974 refresh_watermarks(self);
975}
976
977static void alignment_callback(GtkWidget *tb, gpointer user_data)
978{
979 int index = -1;
980 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
982
983 if(darktable.gui->reset) return;
985
986
987 for(int i = 0; i < 9; i++)
988 {
989 /* block signal handler */
990 g_signal_handlers_block_by_func(g->align[i], alignment_callback, user_data);
991
992 if(GTK_WIDGET(g->align[i]) == tb)
993 {
994 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->align[i]), TRUE);
995 index = i;
996 }
997 else
998 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->align[i]), FALSE);
999
1000 /* unblock signal handler */
1001 g_signal_handlers_unblock_by_func(g->align[i], alignment_callback, user_data);
1002 }
1003 p->alignment = index;
1005}
1006
1007static void text_callback(GtkWidget *entry, gpointer user_data)
1008{
1009 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
1010 if(darktable.gui->reset) return;
1012 g_strlcpy(p->text, gtk_entry_get_text(GTK_ENTRY(entry)), sizeof(p->text));
1013 dt_conf_set_string("plugins/darkroom/watermark/text", p->text);
1015}
1016
1017static void colorpick_color_set(GtkColorButton *widget, gpointer user_data)
1018{
1019 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
1020 if(darktable.gui->reset) return;
1022
1023 GdkRGBA c;
1024 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &c);
1025 p->color[0] = c.red;
1026 p->color[1] = c.green;
1027 p->color[2] = c.blue;
1028
1029 dt_conf_set_float("plugins/darkroom/watermark/color_red", p->color[0]);
1030 dt_conf_set_float("plugins/darkroom/watermark/color_green", p->color[1]);
1031 dt_conf_set_float("plugins/darkroom/watermark/color_blue", p->color[2]);
1033}
1034
1035static void fontsel_callback(GtkWidget *button, gpointer user_data)
1036{
1037 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
1038 if(darktable.gui->reset) return;
1040
1041 gchar *fontname = gtk_font_chooser_get_font(GTK_FONT_CHOOSER(button));
1042 g_strlcpy(p->font, fontname, sizeof(p->font));
1043 dt_free(fontname);
1044 dt_conf_set_string("plugins/darkroom/watermark/font", p->font);
1046}
1047
1050{
1053
1054 d->opacity = p->opacity;
1055 d->scale = p->scale;
1056 d->rotate = p->rotate;
1057 d->xoffset = p->xoffset;
1058 d->yoffset = p->yoffset;
1059 d->alignment = p->alignment;
1060 d->sizeto = p->sizeto;
1061 memset(d->filename, 0, sizeof(d->filename));
1062 g_strlcpy(d->filename, p->filename, sizeof(d->filename));
1063 memset(d->text, 0, sizeof(d->text));
1064 g_strlcpy(d->text, p->text, sizeof(d->text));
1065 for (int k=0; k<3; k++)
1066 d->color[k] = p->color[k];
1067 memset(d->font, 0, sizeof(d->font));
1068 g_strlcpy(d->font, p->font, sizeof(d->font));
1069
1070// fprintf(stderr, "Commit params: %s...\n",d->filename);
1071}
1072
1074{
1076 piece->data_size = sizeof(dt_iop_watermark_data_t);
1077}
1078
1080{
1081 dt_free_align(piece->data);
1082 piece->data = NULL;
1083}
1084
1085
1086void gui_update(struct dt_iop_module_t *self)
1087{
1090 for(int i = 0; i < 9; i++)
1091 {
1092 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->align[i]), FALSE);
1093 }
1094 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->align[p->alignment]), TRUE);
1095 _combo_box_set_active_text(g, p->filename);
1096 gtk_entry_set_text(GTK_ENTRY(g->text), p->text);
1097 GdkRGBA color = (GdkRGBA){.red = p->color[0], .green = p->color[1], .blue = p->color[2], .alpha = 1.0 };
1098 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(g->colorpick), &color);
1099 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(g->fontsel), p->font);
1100}
1101
1103{
1104 dt_iop_default_init(module);
1105}
1106
1108{
1109 dt_iop_watermark_params_t *d = module->default_params;
1110 g_strlcpy(d->filename, "simple-text.svg", sizeof(d->filename));
1111 g_strlcpy(d->font, "DejaVu Sans 10", sizeof(d->font));
1112}
1113
1114void gui_init(struct dt_iop_module_t *self)
1115{
1118
1119 self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1120
1121 GtkGrid *grid = GTK_GRID(gtk_grid_new());
1122 gtk_grid_set_row_spacing(grid, DT_GUI_BOX_SPACING);
1123 gtk_grid_set_column_spacing(grid, DT_GUI_BOX_SPACING);
1124 int line = 0;
1125
1126 // Add the marker combobox
1127 gchar configdir[PATH_MAX] = { 0 };
1128 gchar datadir[PATH_MAX] = { 0 };
1129 dt_loc_get_datadir(datadir, sizeof(datadir));
1130 dt_loc_get_user_config_dir(configdir, sizeof(configdir));
1131
1132 GtkWidget *label = dtgtk_reset_label_new(_("marker"), self, &p->filename, sizeof(p->filename));
1134 gtk_widget_set_hexpand(GTK_WIDGET(g->watermarks), TRUE);
1135 char *tooltip = g_strdup_printf(_("SVG watermarks in %s/watermarks or %s/watermarks"), configdir, datadir);
1136 gtk_widget_set_tooltip_text(g->watermarks, tooltip);
1138 g->refresh = dtgtk_button_new(dtgtk_cairo_paint_refresh, 0, NULL);
1139
1140 gtk_grid_attach(grid, label, 0, line++, 1, 1);
1141 gtk_grid_attach_next_to(grid, g->watermarks, label, GTK_POS_RIGHT, 1, 1);
1142 gtk_grid_attach_next_to(grid, g->refresh, g->watermarks, GTK_POS_RIGHT, 1, 1);
1143
1144 // Simple text
1145 label = dt_ui_label_new(_("text"));
1146 g->text = gtk_entry_new();
1148 gtk_entry_set_width_chars(GTK_ENTRY(g->text), 1);
1149 gtk_widget_set_tooltip_text(g->text, _("text string, tag:\n$(WATERMARK_TEXT)"));
1150 const char *str = dt_conf_get_string_const("plugins/darkroom/watermark/text");
1151 gtk_entry_set_text(GTK_ENTRY(g->text), str);
1152
1153 gtk_grid_attach(grid, label, 0, line++, 1, 1);
1154 gtk_grid_attach_next_to(grid, g->text, label, GTK_POS_RIGHT, 2, 1);
1155
1156 // Text font
1157 label = dtgtk_reset_label_new(_("font"), self, &p->font, sizeof(p->font));
1158 str = dt_conf_get_string_const("plugins/darkroom/watermark/font");
1159 g->fontsel = gtk_font_button_new_with_font(str==NULL?"DejaVu Sans 10":str);
1160 GtkWidget *child = dt_gui_container_first_child(GTK_CONTAINER(gtk_bin_get_child(GTK_BIN(g->fontsel))));
1161 gtk_label_set_ellipsize(GTK_LABEL(child), PANGO_ELLIPSIZE_MIDDLE);
1162 gtk_widget_set_tooltip_text(g->fontsel, _("text font, tags:\n$(WATERMARK_FONT_FAMILY)\n"
1163 "$(WATERMARK_FONT_STYLE)\n$(WATERMARK_FONT_WEIGHT)"));
1164 gtk_font_button_set_show_size (GTK_FONT_BUTTON(g->fontsel), FALSE);
1165
1166 gtk_grid_attach(grid, label, 0, line++, 1, 1);
1167 gtk_grid_attach_next_to(grid, g->fontsel, label, GTK_POS_RIGHT, 2, 1);
1168
1169 // Watermark color
1170 float red = dt_conf_get_float("plugins/darkroom/watermark/color_red");
1171 float green = dt_conf_get_float("plugins/darkroom/watermark/color_green");
1172 float blue = dt_conf_get_float("plugins/darkroom/watermark/color_blue");
1173 GdkRGBA color = (GdkRGBA){.red = red, .green = green, .blue = blue, .alpha = 1.0 };
1174
1175 label = dtgtk_reset_label_new(_("color"), self, &p->color, 3 * sizeof(float));
1176 g->colorpick = gtk_color_button_new_with_rgba(&color);
1177 gtk_widget_set_tooltip_text(g->colorpick, _("watermark color, tag:\n$(WATERMARK_COLOR)"));
1178 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(g->colorpick), FALSE);
1179 gtk_color_button_set_title(GTK_COLOR_BUTTON(g->colorpick), _("select watermark color"));
1180 g->color_picker_button = dt_color_picker_new(self, DT_COLOR_PICKER_POINT, NULL);
1181 gtk_widget_set_tooltip_text(GTK_WIDGET(g->color_picker_button), _("pick color from image"));
1182
1183 gtk_grid_attach(grid, label, 0, line++, 1, 1);
1184 gtk_grid_attach_next_to(grid, g->colorpick, label, GTK_POS_RIGHT, 1, 1);
1185 gtk_grid_attach_next_to(grid, g->color_picker_button, g->colorpick, GTK_POS_RIGHT, 1, 1);
1186
1187 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(grid), TRUE, TRUE, 0);
1188
1189 // Add opacity/scale sliders to table
1190 g->opacity = dt_bauhaus_slider_from_params(self, N_("opacity"));
1191 dt_bauhaus_slider_set_format(g->opacity, "%");
1192
1193 gtk_box_pack_start(GTK_BOX(self->widget), dt_ui_section_label_new(_("placement")), TRUE, TRUE, 0);
1194
1195 // rotate
1196 g->rotate = dt_bauhaus_slider_from_params(self, "rotate");
1197 dt_bauhaus_slider_set_format(g->rotate, "\302\260");
1198
1199 // scale
1200 g->scale = dt_bauhaus_slider_from_params(self, N_("scale"));
1201 dt_bauhaus_slider_set_soft_max(g->scale, 100.0);
1202 dt_bauhaus_slider_set_format(g->scale, "%");
1203
1204 // scale-on
1205 g->sizeto = dt_bauhaus_combobox_from_params(self, "sizeto");
1206 gtk_widget_set_tooltip_text(g->sizeto, _("size is relative to"));
1207
1208 // Create the 3x3 gtk table toggle button table...
1209 GtkWidget *bat = gtk_grid_new();
1210 label = dtgtk_reset_label_new(_("alignment"), self, &p->alignment, sizeof(p->alignment));
1211 gtk_grid_attach(GTK_GRID(bat), label, 0, 0, 1, 3);
1212 gtk_widget_set_hexpand(label, TRUE);
1213 gtk_grid_set_row_spacing(GTK_GRID(bat), DT_GUI_BOX_SPACING);
1214 gtk_grid_set_column_spacing(GTK_GRID(bat), DT_GUI_BOX_SPACING);
1215 for(int i = 0; i < 9; i++)
1216 {
1218 gtk_grid_attach(GTK_GRID(bat), GTK_WIDGET(g->align[i]), 1 + i%3, i/3, 1, 1);
1219 g_signal_connect(G_OBJECT(g->align[i]), "toggled", G_CALLBACK(alignment_callback), self);
1220 }
1221
1222 gtk_box_pack_start(GTK_BOX(self->widget), bat, FALSE, FALSE, 0);
1223
1224 // x/y offset
1225 g->x_offset = dt_bauhaus_slider_from_params(self, "xoffset");
1226 dt_bauhaus_slider_set_digits(g->x_offset, 3);
1227 g->y_offset = dt_bauhaus_slider_from_params(self, "yoffset");
1228 dt_bauhaus_slider_set_digits(g->y_offset, 3);
1229
1230 // Let's add some tooltips and hook up some signals...
1231 gtk_widget_set_tooltip_text(g->opacity, _("the opacity of the watermark"));
1232 gtk_widget_set_tooltip_text(g->scale, _("the scale of the watermark"));
1233 gtk_widget_set_tooltip_text(g->rotate, _("the rotation of the watermark"));
1234
1235 refresh_watermarks(self);
1236
1237 g_signal_connect(G_OBJECT(g->watermarks), "value-changed", G_CALLBACK(watermark_callback), self);
1238 g_signal_connect(G_OBJECT(g->refresh), "clicked", G_CALLBACK(refresh_callback), self);
1239 g_signal_connect(G_OBJECT(g->text), "changed", G_CALLBACK(text_callback), self);
1240 g_signal_connect(G_OBJECT(g->colorpick), "color-set", G_CALLBACK(colorpick_color_set), self);
1241 g_signal_connect(G_OBJECT(g->fontsel), "font-set", G_CALLBACK(fontsel_callback), self);
1242}
1243
1245{
1247 g_list_free_full(g->watermarks_filenames, dt_free_gpointer);
1248 g->watermarks_filenames = NULL;
1249
1251}
1252
1253// clang-format off
1254// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1255// vim: shiftwidth=2 expandtab tabstop=2 cindent
1256// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1257// 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
const char * extension(dt_imageio_module_data_t *data)
Definition avif.c:645
void dt_bauhaus_slider_set_digits(GtkWidget *widget, int val)
Definition bauhaus.c:3534
void dt_bauhaus_combobox_clear(GtkWidget *widget)
Definition bauhaus.c:2189
int dt_bauhaus_combobox_get(GtkWidget *widget)
Definition bauhaus.c:2347
void dt_bauhaus_slider_set_soft_max(GtkWidget *widget, float val)
Definition bauhaus.c:1624
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2301
GtkWidget * dt_bauhaus_combobox_new(dt_bauhaus_t *bh, dt_gui_module_t *self)
Definition bauhaus.c:1842
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3598
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
Definition bauhaus.c:2016
GtkWidget * dtgtk_button_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
Definition button.c:134
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
@ 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
const dt_colormatrix_t dt_aligned_pixel_t out
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.
int type
void dt_conf_set_float(const char *name, float val)
float dt_conf_get_float(const char *name)
void dt_conf_set_string(const char *name, const char *val)
const char * dt_conf_get_string_const(const char *name)
darktable_t darktable
Definition darktable.c:181
#define dt_free_align(ptr)
Definition darktable.h:481
static void * dt_calloc_align(size_t size)
Definition darktable.h:488
static void dt_free_gpointer(gpointer ptr)
Definition darktable.h:463
#define dt_free(ptr)
Definition darktable.h:456
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define __DT_CLONE_TARGETS__
Definition darktable.h:367
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:258
#define PATH_MAX
Definition darktable.h:1062
#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 darktable.h:281
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:41
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
Definition dtgtk/paint.h:71
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:374
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:364
void dt_loc_get_datadir(char *datadir, size_t bufsize)
void dt_loc_get_user_config_dir(char *configdir, size_t bufsize)
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)
Definition format.c:57
@ TYPE_FLOAT
Definition format.h:46
GtkWidget * dt_gui_container_first_child(GtkContainer *container)
Definition gtk.c:2882
void dt_accels_disconnect_on_text_input(GtkWidget *widget)
Disconnects accels when a text or search entry gets the focus, and reconnects them when it looses it....
Definition gtk.c:3225
static GtkWidget * dt_ui_section_label_new(const gchar *str)
Definition gtk.h:451
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
static GtkWidget * dt_ui_label_new(const gchar *str)
Definition gtk.h:461
#define DT_GUI_MODULE(x)
const char * tooltip
Definition image.h:251
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:87
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:316
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
Definition imageop.c:3141
#define IOP_GUI_FREE
Definition imageop.h:602
@ IOP_FLAGS_INCLUDE_IN_STYLES
Definition imageop.h:166
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:167
@ IOP_GROUP_EFFECTS
Definition imageop.h:142
#define IOP_GUI_ALLOC(module)
Definition imageop.h:599
@ IOP_TAG_DECORATION
Definition imageop.h:152
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
Definition imageop_gui.c:77
GtkWidget * dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *param)
void *const ovoid
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
#define M_PI
Definition math.h:45
uint32_t dt_lib_export_metadata_get_conf_flags(void)
GtkWidget * dtgtk_reset_label_new(const gchar *text, dt_iop_module_t *module, void *param, int param_size)
Definition resetlabel.c:58
struct _GtkWidget GtkWidget
Definition splash.h:29
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_gui_gtk_t * gui
Definition darktable.h:775
dt_pthread_mutex_t plugin_threadsafe
Definition darktable.h:793
struct dt_bauhaus_t * bauhaus
Definition darktable.h:778
struct dt_develop_t * develop
Definition darktable.h:770
struct dt_iop_module_t *void * data
struct dt_develop_t * dev
dt_image_t image_storage
Definition develop.h:259
int32_t reset
Definition gtk.h:172
int32_t id
Definition image.h:319
unsigned int channels
Definition format.h:54
dt_iop_buffer_type_t datatype
Definition format.h:56
dt_iop_params_t * default_params
Definition imageop.h:307
GtkWidget * widget
Definition imageop.h:337
dt_iop_gui_data_t * gui_data
Definition imageop.h:311
dt_aligned_pixel_t picked_color
Definition imageop.h:272
dt_iop_params_t * params
Definition imageop.h:307
Region of interest passed through the pixelpipe.
Definition imageop.h:72
double scale
Definition imageop.h:74
dt_iop_watermark_base_scale_t sizeto
Definition watermark.c:142
GtkWidget * color_picker_button
Definition watermark.c:161
dt_iop_watermark_base_scale_t sizeto
Definition watermark.c:124
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:136
RsvgDimensionData dt_get_svg_dimension(RsvgHandle *svg)
Definition utility.c:950
void dt_render_svg(RsvgHandle *svg, cairo_t *cr, double width, double height, double offset_x, double offset_y)
Definition utility.c:983
int operation_tags()
Definition watermark.c:354
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:1048
void init(dt_iop_module_t *module)
Definition watermark.c:1102
static void colorpick_color_set(GtkColorButton *widget, gpointer user_data)
Definition watermark.c:1017
const char ** description(struct dt_iop_module_t *self)
Definition watermark.c:335
int default_group()
Definition watermark.c:349
__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:497
static void watermark_callback(GtkWidget *tb, gpointer user_data)
Definition watermark.c:865
void reload_defaults(dt_iop_module_t *module)
Definition watermark.c:1107
static void _text_color_font_set_sensitive(dt_iop_watermark_gui_data_t *g, gchar *filename)
Definition watermark.c:373
static void fontsel_callback(GtkWidget *button, gpointer user_data)
Definition watermark.c:1035
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition watermark.c:1073
static gchar * _string_escape(const gchar *string)
Definition watermark.c:402
dt_iop_watermark_base_scale_t
Definition watermark.c:98
@ DT_SCALE_SMALLER_BORDER
Definition watermark.c:101
@ DT_SCALE_IMAGE
Definition watermark.c:99
@ DT_SCALE_LARGER_BORDER
Definition watermark.c:100
const char * name()
Definition watermark.c:330
static void refresh_watermarks(dt_iop_module_t *self)
Definition watermark.c:945
void gui_update(struct dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
Definition watermark.c:1086
static gchar * _string_substitute(gchar *string, const gchar *search, const gchar *replace)
Definition watermark.c:418
static void refresh_callback(GtkWidget *tb, gpointer user_data)
Definition watermark.c:971
void gui_init(struct dt_iop_module_t *self)
Definition watermark.c:1114
static void text_callback(GtkWidget *entry, gpointer user_data)
Definition watermark.c:1007
static void alignment_callback(GtkWidget *tb, gpointer user_data)
Definition watermark.c:977
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition watermark.c:359
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:364
int flags()
Definition watermark.c:344
void gui_cleanup(struct dt_iop_module_t *self)
Definition watermark.c:1244
dt_iop_watermark_type_t
Definition watermark.c:105
@ DT_WTM_PNG
Definition watermark.c:107
@ DT_WTM_SVG
Definition watermark.c:106
static void load_watermarks(const char *basedir, dt_iop_watermark_gui_data_t *g)
Definition watermark.c:905
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition watermark.c:1079
static void _combo_box_set_active_text(dt_iop_watermark_gui_data_t *g, gchar *text)
Definition watermark.c:386
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:879
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:164
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:427