Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
lightroom.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2013 johannes hanika.
4 Copyright (C) 2013-2017, 2019-2021 Pascal Obry.
5 Copyright (C) 2013-2014, 2016 Roman Lebedev.
6 Copyright (C) 2013 Simon Spannagel.
7 Copyright (C) 2013 Thomas Pryds.
8 Copyright (C) 2013-2014, 2016-2018 Tobias Ellinghaus.
9 Copyright (C) 2015 Bruce Guenter.
10 Copyright (C) 2015 Jérémy Rosen.
11 Copyright (C) 2018-2019 Edgardo Hoszowski.
12 Copyright (C) 2018-2021 Philippe Weyland.
13 Copyright (C) 2019 Heiko Bauke.
14 Copyright (C) 2019 Jacques Le Clerc.
15 Copyright (C) 2020 Aldric Renaudin.
16 Copyright (C) 2020-2021 Hubert Kowalski.
17 Copyright (C) 2021 Hanno Schwalm.
18 Copyright (C) 2022, 2025 Aurélien PIERRE.
19 Copyright (C) 2022 Martin Bařinka.
20 Copyright (C) 2023 Guillaume Stutin.
21 Copyright (C) 2023 Ricky Moon.
22
23 darktable is free software: you can redistribute it and/or modify
24 it under the terms of the GNU General Public License as published by
25 the Free Software Foundation, either version 3 of the License, or
26 (at your option) any later version.
27
28 darktable is distributed in the hope that it will be useful,
29 but WITHOUT ANY WARRANTY; without even the implied warranty of
30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 GNU General Public License for more details.
32
33 You should have received a copy of the GNU General Public License
34 along with darktable. If not, see <http://www.gnu.org/licenses/>.
35*/
36
38#include "develop/lightroom.h"
40#include "common/curve_tools.h"
41#include "system/macros.h"
42#include "system/mem_alloc.h"
43#include "common/paths.h"
44#include "control/signal.h"
45#include "common/utility.h"
46#include "history/history.h"
48#include "metadata/ratings.h"
49#include "metadata/tags.h"
50#include "metadata/metadata.h"
52#include "develop/develop.h"
53
54#include <libxml/parser.h>
55#include <libxml/xpath.h>
56#include <libxml/xpathInternals.h>
57#include <stdlib.h>
58#include <string.h>
59
60// copy here the iop params struct with the actual version. This is so to
61// be as independent as possible of any iop evolutions. Indeed, we create
62// the iop params into the database for a specific version. We then ask
63// for a reload of the history parameter. If the iop has evolved since then
64// the legacy circuitry will be called to convert the parameters.
65//
66// to add a new iop:
67// 1. copy the struct
68// 2. add LRDT_<iop_name>_VERSION with corresponding module version
69// 3. use this version to pass in dt_add_hist()
70
71#define LRDT_CLIPPING_VERSION 5
80
81#define LRDT_FLIP_VERSION 2
86
87#define LRDT_EXPOSURE_VERSION 2
92
93#define LRDT_GRAIN_VERSION 1
101
108
115
117{
118 float x;
119 float y;
121
122#define LRDT_VIGNETTE_VERSION 3
124{
125 float scale; // 0 - 100 Inner radius, percent of largest image dimension
126 float falloff_scale; // 0 - 100 Radius for falloff -- outer radius = inner radius + falloff_scale
127 float brightness; // -1 - 1 Strength of brightness reduction
128 float saturation; // -1 - 1 Strength of saturation reduction
129 dt_iop_vector_2d_t center; // Center of vignette
130 gboolean autoratio; //
131 float whratio; // 0-1 = width/height ratio, 1-2 = height/width ratio + 1
132 float shape;
133 int dithering; // if and how to perform dithering
135
136#define LRDT_SPOTS_VERSION 1
137#define MAX_SPOTS 32
138
139typedef struct spot_t
140{
141 // position of the spot
142 float x, y;
143 // position to clone from
144 float xc, yc;
145 float radius;
147
153
154#define LRDT_TONECURVE_VERSION 3
155#define DT_IOP_TONECURVE_MAXNODES 20
163
169
179
180#define LRDT_COLORZONES_VERSION 2
181#define DT_IOP_COLORZONES_BANDS 8
182
189
195
196#define LRDT_SPLITTONING_VERSION 1
198{
203 float balance; // center luminance of gradient
204 float compress; // Compress range
206
207#define LRDT_BILAT_VERSION 1
214
215
216#define LRDT_COLORIN_VERSION 1
217#define DT_IOP_COLOR_ICC_LEN_V1 100
218
224
225#undef DT_IOP_COLOR_ICC_LEN_V1
226
227//
228// end of iop structs
229//
230
231// the blend params for Lr import, not used in this mode (mode=0), as for iop generate the blend params for
232// the
233// version specified above.
234
235#define LRDT_BLEND_VERSION 4
236#define DEVELOP_BLENDIF_SIZE 16
237
238typedef struct dt_develop_blend_params_t
239{
241 uint32_t mode;
243 float opacity;
245 uint32_t mask_id;
247 uint32_t blendif;
249 float radius;
253
254//
255// end of blend_params
256//
257
258typedef struct lr2dt
259{
260 float lr, dt;
262
263char *dt_get_lightroom_xmp(int32_t imgid)
264{
265 char pathname[DT_MAX_FILENAME_LEN];
266 gboolean from_cache = TRUE;
267
268 // Get full pathname
269 dt_image_full_path(imgid, pathname, DT_MAX_FILENAME_LEN, &from_cache, __FUNCTION__);
270
271 // Look for extension
272 char *pos = strrchr(pathname, '.');
273
274 if(IS_NULL_PTR(pos))
275 return NULL;
276
277 // If found, replace extension with xmp
278 strncpy(pos + 1, "xmp", 4);
279 if(g_file_test(pathname, G_FILE_TEST_EXISTS))
280 return g_strdup(pathname);
281
282 strncpy(pos + 1, "XMP", 4);
283 if(g_file_test(pathname, G_FILE_TEST_EXISTS))
284 return g_strdup(pathname);
285
286 return NULL;
287}
288
289static float get_interpolate(lr2dt_t lr2dt_table[], float value)
290{
291 int k = 0;
292
293 while(lr2dt_table[k + 1].lr < value) k++;
294
295 return lr2dt_table[k].dt
296 + ((value - lr2dt_table[k].lr) / (lr2dt_table[k + 1].lr - lr2dt_table[k].lr))
297 * (lr2dt_table[k + 1].dt - lr2dt_table[k].dt);
298}
299
300static float lr2dt_blacks(float value)
301{
302 lr2dt_t lr2dt_blacks_table[]
303 = { { -100, 0.020 }, { -50, 0.005 }, { 0, 0 }, { 50, -0.005 }, { 100, -0.010 } };
304
305 return get_interpolate(lr2dt_blacks_table, value);
306}
307
308static float lr2dt_vignette_gain(float value)
309{
310 lr2dt_t lr2dt_vignette_table[] = { { -100, -1 }, { -50, -0.7 }, { 0, 0 }, { 50, 0.5 }, { 100, 1 } };
311
312 return get_interpolate(lr2dt_vignette_table, value);
313}
314
316{
317 lr2dt_t lr2dt_vignette_table[] = { { 0, 74 }, { 4, 75 }, { 25, 85 }, { 50, 100 }, { 100, 100 } };
318
319 return get_interpolate(lr2dt_vignette_table, value);
320}
321
322static float lr2dt_grain_amount(float value)
323{
324 lr2dt_t lr2dt_grain_table[] = { { 0, 0 }, { 25, 20 }, { 50, 40 }, { 100, 80 } };
325
326 return get_interpolate(lr2dt_grain_table, value);
327}
328
329static float lr2dt_grain_frequency(float value)
330{
331 lr2dt_t lr2dt_grain_table[] = { { 0, 100 }, { 50, 100 }, { 75, 400 }, { 100, 800 } };
332
333 return get_interpolate(lr2dt_grain_table, value) / 53.3;
334}
335
337{
338 lr2dt_t lr2dt_splittoning_table[] = { { -100, 100 }, { 0, 0 }, { 100, 0 } };
339
340 return get_interpolate(lr2dt_splittoning_table, value);
341}
342
343static float lr2dt_clarity(float value)
344{
345 lr2dt_t lr2dt_clarity_table[] = { { -100, -.650 }, { 0, 0 }, { 100, .650 } };
346
347 return get_interpolate(lr2dt_clarity_table, value);
348}
349
350static void dt_add_hist(int32_t imgid, char *operation, dt_iop_params_t *params, int params_size, char *imported,
351 size_t imported_len, int version, int *import_count)
352{
353 dt_develop_blend_params_t blend_params = { 0 };
354
355 const int32_t num = dt_history_repository_get_next_num(imgid);
356 dt_history_repository_write_item(imgid, num, operation, params, params_size, version, 1,
357 &blend_params, sizeof(dt_develop_blend_params_t), LRDT_BLEND_VERSION, 0, " ");
358 dt_history_repository_set_end(imgid, num + 1);
359
360 if(imported[0]) g_strlcat(imported, ", ", imported_len);
361 g_strlcat(imported, dt_iop_get_localized_name(operation), imported_len);
362 (*import_count)++;
363}
364
365#define MAX_PTS 20
366
374
427
428// three helper functions for parsing RetouchInfo entries. sscanf doesn't work due to floats.
429static gboolean _read_float(const char **startptr, const char *key, float *value)
430{
431 const char *iter = *startptr;
432 while(*iter == ' ') iter++;
433 if(!g_str_has_prefix(iter, key))
434 return FALSE;
435 iter += strlen(key);
436 while(*iter == ' ') iter++;
437 if(*iter++ != '=')
438 return FALSE;
439 while(*iter == ' ') iter++;
440 *value = g_ascii_strtod(iter, (char **)startptr);
441 return iter != *startptr;
442}
443
444static gboolean _skip_key_value_pair(const char **startptr, const char *key)
445{
446 const char *iter = *startptr;
447 while(*iter == ' ') iter++;
448 if(!g_str_has_prefix(iter, key))
449 return FALSE;
450 iter += strlen(key);
451 while(*iter == ' ') iter++;
452 if(*iter++ != '=')
453 return FALSE;
454 while(*iter == ' ') iter++;
455 while((*iter >= 'a' && *iter <= 'z') || (*iter >= 'A' && *iter <= 'Z')) iter++;
456 *startptr = iter;
457 return TRUE;
458}
459
460static gboolean _skip_comma(const char **startptr)
461{
462 return *(*startptr)++ == ',';
463}
464
465/* lrop handle the Lr operation and convert it as a dt iop */
466static void _lrop(const dt_develop_t *dev, const xmlDocPtr doc, const int32_t imgid,
467 const xmlChar *name, const xmlChar *value, const xmlNodePtr node, lr_data_t *data)
468{
469 const float hfactor = 3.0 / 9.0; // hue factor adjustment (use 3 out of 9 boxes in colorzones)
470 const float lfactor = 4.0 / 9.0; // lightness factor adjustment (use 4 out of 9 boxes in colorzones)
471
472 if(value)
473 {
474 if(!xmlStrcmp(name, (const xmlChar *)"CropTop"))
475 data->pc.cy = g_ascii_strtod((char *)value, NULL);
476 else if(!xmlStrcmp(name, (const xmlChar *)"CropRight"))
477 data->pc.cw = g_ascii_strtod((char *)value, NULL);
478 else if(!xmlStrcmp(name, (const xmlChar *)"CropLeft"))
479 data->pc.cx = g_ascii_strtod((char *)value, NULL);
480 else if(!xmlStrcmp(name, (const xmlChar *)"CropBottom"))
481 data->pc.ch = g_ascii_strtod((char *)value, NULL);
482 else if(!xmlStrcmp(name, (const xmlChar *)"CropAngle"))
483 data->pc.angle = -g_ascii_strtod((char *)value, NULL);
484 else if(!xmlStrcmp(name, (const xmlChar *)"ImageWidth"))
485 data->iwidth = atoi((char *)value);
486 else if(!xmlStrcmp(name, (const xmlChar *)"ImageLength"))
487 data->iheight = atoi((char *)value);
488 else if(!xmlStrcmp(name, (const xmlChar *)"Orientation"))
489 {
490 data->orientation = atoi((char *)value);
494 data->has_flip = TRUE;
495 }
496 else if(!xmlStrcmp(name, (const xmlChar *)"HasCrop"))
497 {
498 if(!xmlStrcmp(value, (const xmlChar *)"True")) data->has_crop = TRUE;
499 }
500 else if(!xmlStrcmp(name, (const xmlChar *)"Blacks2012"))
501 {
502 int v = atoi((char *)value);
503 if(v != 0)
504 {
505 data->has_exposure = TRUE;
506 data->pe.black = lr2dt_blacks((float)v);
507 }
508 }
509 else if(!xmlStrcmp(name, (const xmlChar *)"Exposure2012"))
510 {
511 float v = g_ascii_strtod((char *)value, NULL);
512 if(v != 0.0)
513 {
514 data->has_exposure = TRUE;
515 data->pe.exposure = v;
516 }
517 }
518 else if(!xmlStrcmp(name, (const xmlChar *)"PostCropVignetteAmount"))
519 {
520 int v = atoi((char *)value);
521 if(v != 0)
522 {
523 data->has_vignette = TRUE;
524 data->pv.brightness = lr2dt_vignette_gain((float)v);
525 }
526 }
527 else if(!xmlStrcmp(name, (const xmlChar *)"PostCropVignetteMidpoint"))
528 {
529 int v = atoi((char *)value);
530 data->pv.scale = lr2dt_vignette_midpoint((float)v);
531 }
532 else if(!xmlStrcmp(name, (const xmlChar *)"PostCropVignetteStyle"))
533 {
534 int v = atoi((char *)value);
535 if(v == 1) // Highlight Priority
536 data->pv.saturation = -0.300;
537 else // Color Priority & Paint Overlay
538 data->pv.saturation = -0.200;
539 }
540 else if(!xmlStrcmp(name, (const xmlChar *)"PostCropVignetteFeather"))
541 {
542 int v = atoi((char *)value);
543 if(v != 0) data->pv.falloff_scale = (float)v;
544 }
545 else if(!xmlStrcmp(name, (const xmlChar *)"PostCropVignetteRoundness"))
546 {
547 int v = atoi((char *)value);
548 data->crop_roundness = (float)v;
549 }
550 else if(!xmlStrcmp(name, (const xmlChar *)"GrainAmount"))
551 {
552 int v = atoi((char *)value);
553 if(v != 0)
554 {
555 data->has_grain = TRUE;
556 data->pg.strength = lr2dt_grain_amount((float)v);
557 }
558 }
559 else if(!xmlStrcmp(name, (const xmlChar *)"GrainFrequency"))
560 {
561 int v = atoi((char *)value);
562 if(v != 0) data->pg.scale = lr2dt_grain_frequency((float)v);
563 }
564 else if(!xmlStrcmp(name, (const xmlChar *)"ParametricShadows"))
565 {
566 data->ptc_value[0] = atoi((char *)value);
567 }
568 else if(!xmlStrcmp(name, (const xmlChar *)"ParametricDarks"))
569 {
570 data->ptc_value[1] = atoi((char *)value);
571 }
572 else if(!xmlStrcmp(name, (const xmlChar *)"ParametricLights"))
573 {
574 data->ptc_value[2] = atoi((char *)value);
575 }
576 else if(!xmlStrcmp(name, (const xmlChar *)"ParametricHighlights"))
577 {
578 data->ptc_value[3] = atoi((char *)value);
579 }
580 else if(!xmlStrcmp(name, (const xmlChar *)"ParametricShadowSplit"))
581 {
582 data->ptc_split[0] = g_ascii_strtod((char *)value, NULL) / 100.0;
583 }
584 else if(!xmlStrcmp(name, (const xmlChar *)"ParametricMidtoneSplit"))
585 {
586 data->ptc_split[1] = g_ascii_strtod((char *)value, NULL) / 100.0;
587 }
588 else if(!xmlStrcmp(name, (const xmlChar *)"ParametricHighlightSplit"))
589 {
590 data->ptc_split[2] = g_ascii_strtod((char *)value, NULL) / 100.0;
591 }
592 else if(!xmlStrcmp(name, (const xmlChar *)"ToneCurveName2012"))
593 {
594 if(!xmlStrcmp(value, (const xmlChar *)"Linear"))
595 data->curve_kind = linear;
596 else if(!xmlStrcmp(value, (const xmlChar *)"Medium Contrast"))
598 else if(!xmlStrcmp(value, (const xmlChar *)"Strong Contrast"))
600 else if(!xmlStrcmp(value, (const xmlChar *)"Custom"))
601 data->curve_kind = custom;
602 }
603 else if(!xmlStrcmp(name, (const xmlChar *)"SaturationAdjustmentRed"))
604 {
605 int v = atoi((char *)value);
606 if(v != 0) data->has_colorzones = TRUE;
607 data->pcz.equalizer_y[1][0] = 0.5 + (float)v / 200.0;
608 }
609 else if(!xmlStrcmp(name, (const xmlChar *)"SaturationAdjustmentOrange"))
610 {
611 int v = atoi((char *)value);
612 if(v != 0) data->has_colorzones = TRUE;
613 data->pcz.equalizer_y[1][1] = 0.5 + (float)v / 200.0;
614 }
615 else if(!xmlStrcmp(name, (const xmlChar *)"SaturationAdjustmentYellow"))
616 {
617 int v = atoi((char *)value);
618 if(v != 0) data->has_colorzones = TRUE;
619 data->pcz.equalizer_y[1][2] = 0.5 + (float)v / 200.0;
620 }
621 else if(!xmlStrcmp(name, (const xmlChar *)"SaturationAdjustmentGreen"))
622 {
623 int v = atoi((char *)value);
624 if(v != 0) data->has_colorzones = TRUE;
625 data->pcz.equalizer_y[1][3] = 0.5 + (float)v / 200.0;
626 }
627 else if(!xmlStrcmp(name, (const xmlChar *)"SaturationAdjustmentAqua"))
628 {
629 int v = atoi((char *)value);
630 if(v != 0) data->has_colorzones = TRUE;
631 data->pcz.equalizer_y[1][4] = 0.5 + (float)v / 200.0;
632 }
633 else if(!xmlStrcmp(name, (const xmlChar *)"SaturationAdjustmentBlue"))
634 {
635 int v = atoi((char *)value);
636 if(v != 0) data->has_colorzones = TRUE;
637 data->pcz.equalizer_y[1][5] = 0.5 + (float)v / 200.0;
638 }
639 else if(!xmlStrcmp(name, (const xmlChar *)"SaturationAdjustmentPurple"))
640 {
641 int v = atoi((char *)value);
642 if(v != 0) data->has_colorzones = TRUE;
643 data->pcz.equalizer_y[1][6] = 0.5 + (float)v / 200.0;
644 }
645 else if(!xmlStrcmp(name, (const xmlChar *)"SaturationAdjustmentMagenta"))
646 {
647 int v = atoi((char *)value);
648 if(v != 0) data->has_colorzones = TRUE;
649 data->pcz.equalizer_y[1][7] = 0.5 + (float)v / 200.0;
650 }
651 else if(!xmlStrcmp(name, (const xmlChar *)"LuminanceAdjustmentRed"))
652 {
653 int v = atoi((char *)value);
654 if(v != 0) data->has_colorzones = TRUE;
655 data->pcz.equalizer_y[0][0] = 0.5 + lfactor * (float)v / 200.0;
656 }
657 else if(!xmlStrcmp(name, (const xmlChar *)"LuminanceAdjustmentOrange"))
658 {
659 int v = atoi((char *)value);
660 if(v != 0) data->has_colorzones = TRUE;
661 data->pcz.equalizer_y[0][1] = 0.5 + lfactor * (float)v / 200.0;
662 }
663 else if(!xmlStrcmp(name, (const xmlChar *)"LuminanceAdjustmentYellow"))
664 {
665 int v = atoi((char *)value);
666 if(v != 0) data->has_colorzones = TRUE;
667 data->pcz.equalizer_y[0][2] = 0.5 + lfactor * (float)v / 200.0;
668 }
669 else if(!xmlStrcmp(name, (const xmlChar *)"LuminanceAdjustmentGreen"))
670 {
671 int v = atoi((char *)value);
672 if(v != 0) data->has_colorzones = TRUE;
673 data->pcz.equalizer_y[0][3] = 0.5 + lfactor * (float)v / 200.0;
674 }
675 else if(!xmlStrcmp(name, (const xmlChar *)"LuminanceAdjustmentAqua"))
676 {
677 int v = atoi((char *)value);
678 if(v != 0) data->has_colorzones = TRUE;
679 data->pcz.equalizer_y[0][4] = 0.5 + lfactor * (float)v / 200.0;
680 }
681 else if(!xmlStrcmp(name, (const xmlChar *)"LuminanceAdjustmentBlue"))
682 {
683 int v = atoi((char *)value);
684 if(v != 0) data->has_colorzones = TRUE;
685 data->pcz.equalizer_y[0][5] = 0.5 + lfactor * (float)v / 200.0;
686 }
687 else if(!xmlStrcmp(name, (const xmlChar *)"LuminanceAdjustmentPurple"))
688 {
689 int v = atoi((char *)value);
690 if(v != 0) data->has_colorzones = TRUE;
691 data->pcz.equalizer_y[0][6] = 0.5 + lfactor * (float)v / 200.0;
692 }
693 else if(!xmlStrcmp(name, (const xmlChar *)"LuminanceAdjustmentMagenta"))
694 {
695 int v = atoi((char *)value);
696 if(v != 0) data->has_colorzones = TRUE;
697 data->pcz.equalizer_y[0][7] = 0.5 + lfactor * (float)v / 200.0;
698 }
699 else if(!xmlStrcmp(name, (const xmlChar *)"HueAdjustmentRed"))
700 {
701 int v = atoi((char *)value);
702 if(v != 0) data->has_colorzones = TRUE;
703 data->pcz.equalizer_y[2][0] = 0.5 + hfactor * (float)v / 200.0;
704 }
705 else if(!xmlStrcmp(name, (const xmlChar *)"HueAdjustmentOrange"))
706 {
707 int v = atoi((char *)value);
708 if(v != 0) data->has_colorzones = TRUE;
709 data->pcz.equalizer_y[2][1] = 0.5 + hfactor * (float)v / 200.0;
710 }
711 else if(!xmlStrcmp(name, (const xmlChar *)"HueAdjustmentYellow"))
712 {
713 int v = atoi((char *)value);
714 if(v != 0) data->has_colorzones = TRUE;
715 data->pcz.equalizer_y[2][2] = 0.5 + hfactor * (float)v / 200.0;
716 }
717 else if(!xmlStrcmp(name, (const xmlChar *)"HueAdjustmentGreen"))
718 {
719 int v = atoi((char *)value);
720 if(v != 0) data->has_colorzones = TRUE;
721 data->pcz.equalizer_y[2][3] = 0.5 + hfactor * (float)v / 200.0;
722 }
723 else if(!xmlStrcmp(name, (const xmlChar *)"HueAdjustmentAqua"))
724 {
725 int v = atoi((char *)value);
726 if(v != 0) data->has_colorzones = TRUE;
727 data->pcz.equalizer_y[2][4] = 0.5 + hfactor * (float)v / 200.0;
728 }
729 else if(!xmlStrcmp(name, (const xmlChar *)"HueAdjustmentBlue"))
730 {
731 int v = atoi((char *)value);
732 if(v != 0) data->has_colorzones = TRUE;
733 data->pcz.equalizer_y[2][5] = 0.5 + hfactor * (float)v / 200.0;
734 }
735 else if(!xmlStrcmp(name, (const xmlChar *)"HueAdjustmentPurple"))
736 {
737 int v = atoi((char *)value);
738 if(v != 0) data->has_colorzones = TRUE;
739 data->pcz.equalizer_y[2][6] = 0.5 + hfactor * (float)v / 200.0;
740 }
741 else if(!xmlStrcmp(name, (const xmlChar *)"HueAdjustmentMagenta"))
742 {
743 int v = atoi((char *)value);
744 if(v != 0) data->has_colorzones = TRUE;
745 data->pcz.equalizer_y[2][7] = 0.5 + hfactor * (float)v / 200.0;
746 }
747 else if(!xmlStrcmp(name, (const xmlChar *)"SplitToningShadowHue"))
748 {
749 int v = atoi((char *)value);
750 if(v != 0) data->has_splittoning = TRUE;
751 data->pst.shadow_hue = (float)v / 255.0;
752 }
753 else if(!xmlStrcmp(name, (const xmlChar *)"SplitToningShadowSaturation"))
754 {
755 int v = atoi((char *)value);
756 if(v != 0) data->has_splittoning = TRUE;
757 data->pst.shadow_saturation = (float)v / 100.0;
758 }
759 else if(!xmlStrcmp(name, (const xmlChar *)"SplitToningHighlightHue"))
760 {
761 int v = atoi((char *)value);
762 if(v != 0) data->has_splittoning = TRUE;
763 data->pst.highlight_hue = (float)v / 255.0;
764 }
765 else if(!xmlStrcmp(name, (const xmlChar *)"SplitToningHighlightSaturation"))
766 {
767 int v = atoi((char *)value);
768 if(v != 0) data->has_splittoning = TRUE;
769 data->pst.highlight_saturation = (float)v / 100.0;
770 }
771 else if(!xmlStrcmp(name, (const xmlChar *)"SplitToningBalance"))
772 {
773 float v = g_ascii_strtod((char *)value, NULL);
775 }
776 else if(!xmlStrcmp(name, (const xmlChar *)"Clarity2012"))
777 {
778 int v = atoi((char *)value);
779 if(v != 0)
780 {
781 data->has_bilat = TRUE;
782 data->pbl.detail = lr2dt_clarity((float)v);
783 }
784 }
785 else if(!xmlStrcmp(name, (const xmlChar *)"Rating"))
786 {
787 int v = atoi((char *)value);
788 if(v != 0)
789 {
790 data->rating = v;
791 data->has_rating = TRUE;
792 }
793 }
794 else if(!xmlStrcmp(name, (const xmlChar *)"GPSLatitude"))
795 {
796 double latitude = dt_util_gps_string_to_number((const char *)value);
797 if(!isnan(latitude))
798 {
799 data->lat = latitude;
800 data->has_gps = TRUE;
801 }
802 }
803 else if(!xmlStrcmp(name, (const xmlChar *)"GPSLongitude"))
804 {
805 double longitude = dt_util_gps_string_to_number((const char *)value);
806 if(!isnan(longitude))
807 {
808 data->lon = longitude;
809 data->has_gps = TRUE;
810 }
811 }
812 else if(!xmlStrcmp(name, (const xmlChar *)"Label"))
813 {
814 char *v = g_utf8_casefold((char *)value, -1);
815 if(!g_strcmp0(v, _("red")))
816 data->color = 0;
817 else if(!g_strcmp0(v, _("yellow")))
818 data->color = 1;
819 else if(!g_strcmp0(v, _("green")))
820 data->color = 2;
821 else if(!g_strcmp0(v, _("blue")))
822 data->color = 3;
823 else
824 // just an else here to catch all other cases as on lightroom one can
825 // change the names of labels. So purple and the user's defined labels
826 // will be mapped to purple on darktable.
827 data->color = 4;
828
829 data->has_colorlabel = TRUE;
830 dt_free(v);
831 }
832 }
833 if(IS_NULL_PTR(dev) && (!xmlStrcmp(name, (const xmlChar *)"subject")
834 || !xmlStrcmp(name, (const xmlChar *)"hierarchicalSubject")))
835 {
836 xmlNodePtr tagNode = node;
837
838 gboolean tag_change = FALSE;
839 while(tagNode)
840 {
841 if(!xmlStrcmp(tagNode->name, (const xmlChar *)"li"))
842 {
843 xmlChar *cvalue = xmlNodeListGetString(doc, tagNode->xmlChildrenNode, 1);
844 guint tagid = 0;
845 if(!dt_tag_exists((char *)cvalue, &tagid)) dt_tag_new((char *)cvalue, &tagid);
846
847 if(dt_tag_attach(tagid, imgid, FALSE, FALSE)) tag_change = TRUE;
848 data->has_tags = TRUE;
849 xmlFree(cvalue);
850 }
851 tagNode = tagNode->next;
852 }
854 }
855 else if(!IS_NULL_PTR(dev) && !xmlStrcmp(name, (const xmlChar *)"RetouchInfo"))
856 {
857 xmlNodePtr riNode = node;
858
859 while(riNode)
860 {
861 if(!xmlStrcmp(riNode->name, (const xmlChar *)"li"))
862 {
863 xmlChar *cvalue = xmlNodeListGetString(doc, riNode->xmlChildrenNode, 1);
864 spot_t *p = &data->ps.spot[data->ps.num_spots];
865 float x, y, radius, xc, yc;
866 const char *startptr = (const char *)cvalue;
867 if(_read_float(&startptr, "centerX", &x) &&
868 _skip_comma(&startptr) &&
869 _read_float(&startptr, "centerY", &y) &&
870 _skip_comma(&startptr) &&
871 _read_float(&startptr, "radius", &radius) &&
872 _skip_comma(&startptr) &&
873 _skip_key_value_pair(&startptr, "sourceState") &&
874 _skip_comma(&startptr) &&
875 _read_float(&startptr, "sourceX", &xc) &&
876 _skip_comma(&startptr) &&
877 _read_float(&startptr, "sourceY", &yc))
878 {
879 p->x = x;
880 p->y = y;
881 p->radius = radius;
882 p->xc = xc;
883 p->yc = yc;
884 data->ps.num_spots++;
885 data->has_spots = TRUE;
886 }
887 xmlFree(cvalue);
888 }
889 if(data->ps.num_spots == MAX_SPOTS) break;
890 riNode = riNode->next;
891 }
892 }
893 else if(!IS_NULL_PTR(dev) && !xmlStrcmp(name, (const xmlChar *)"ToneCurvePV2012"))
894 {
895 xmlNodePtr tcNode = node;
896
897 while(tcNode)
898 {
899 if(!xmlStrcmp(tcNode->name, (const xmlChar *)"li"))
900 {
901 xmlChar *cvalue = xmlNodeListGetString(doc, tcNode->xmlChildrenNode, 1);
902
903 if(sscanf((const char *)cvalue, "%d, %d",
904 &(data->curve_pts[data->n_pts][0]), &(data->curve_pts[data->n_pts][1]))) data->n_pts++;
905 xmlFree(cvalue);
906 }
907 if(data->n_pts == MAX_PTS) break;
908 tcNode = tcNode->next;
909 }
910 }
911 else if(IS_NULL_PTR(dev) && !xmlStrcmp(name, (const xmlChar *)"title"))
912 {
913 xmlNodePtr ttlNode = node;
914 while(ttlNode)
915 {
916 if(!xmlStrncmp(ttlNode->name, (const xmlChar *)"li", 2))
917 {
918 xmlChar *cvalue = xmlNodeListGetString(doc, ttlNode->xmlChildrenNode, 1);
919 dt_metadata_set_import(imgid, "Xmp.dc.title", (char *)cvalue);
920 xmlFree(cvalue);
921 }
922 ttlNode = ttlNode->next;
923 }
924 }
925 else if(IS_NULL_PTR(dev) && !xmlStrcmp(name, (const xmlChar *)"description"))
926 {
927 xmlNodePtr desNode = node;
928 while(desNode)
929 {
930 if(!xmlStrncmp(desNode->name, (const xmlChar *)"li", 2))
931 {
932 xmlChar *cvalue = xmlNodeListGetString(doc, desNode->xmlChildrenNode, 1);
933 dt_metadata_set_import(imgid, "Xmp.dc.description", (char *)cvalue);
934 xmlFree(cvalue);
935 }
936 desNode = desNode->next;
937 }
938 }
939 else if(IS_NULL_PTR(dev) && !xmlStrcmp(name, (const xmlChar *)"creator"))
940 {
941 xmlNodePtr creNode = node;
942 while(creNode)
943 {
944 if(!xmlStrncmp(creNode->name, (const xmlChar *)"li", 2))
945 {
946 xmlChar *cvalue = xmlNodeListGetString(doc, creNode->xmlChildrenNode, 1);
947 dt_metadata_set_import(imgid, "Xmp.dc.creator", (char *)cvalue);
948 xmlFree(cvalue);
949 }
950 creNode = creNode->next;
951 }
952 }
953 else if(IS_NULL_PTR(dev) && !xmlStrcmp(name, (const xmlChar *)"rights"))
954 {
955 xmlNodePtr rigNode = node;
956 while(rigNode)
957 {
958 if(!xmlStrncmp(rigNode->name, (const xmlChar *)"li", 2))
959 {
960 xmlChar *cvalue = xmlNodeListGetString(doc, rigNode->xmlChildrenNode, 1);
961 dt_metadata_set_import(imgid, "Xmp.dc.rights", (char *)cvalue);
962 xmlFree(cvalue);
963 }
964 rigNode = rigNode->next;
965 }
966 }
967}
968
969/* _has_list returns true if the node contains a list of value */
970static int _has_list(char *name)
971{
972 return !strcmp(name, "subject")
973 || !strcmp(name, "hierarchicalSubject")
974 || !strcmp(name, "RetouchInfo")
975 || !strcmp(name, "ToneCurvePV2012")
976 || !strcmp(name, "title")
977 || !strcmp(name, "description")
978 || !strcmp(name, "creator")
979 || !strcmp(name, "publisher")
980 || !strcmp(name, "rights");
981};
982
983/* handle a specific xpath */
984static void _handle_xpath(dt_develop_t *dev, xmlDoc *doc, int32_t imgid, xmlXPathContext *ctx, const xmlChar *xpath, lr_data_t *data)
985{
986 xmlXPathObject *xpathObj = xmlXPathEvalExpression(xpath, ctx);
987
988 if (!IS_NULL_PTR(xpathObj))
989 {
990 const xmlNodeSetPtr xnodes = xpathObj->nodesetval;
991 const int n = xnodes->nodeNr;
992
993 for (int k=0; k<n; k++)
994 {
995 const xmlNode *node = xnodes->nodeTab[k];
996
997 if (_has_list((char *)node->name))
998 {
999 xmlNodePtr listnode = node->xmlChildrenNode;
1000 if (listnode) listnode = listnode->next;
1001 if (listnode) listnode = listnode->xmlChildrenNode;
1002 if (listnode) listnode = listnode->next;
1003 if (listnode) _lrop(dev, doc, imgid, node->name, NULL, listnode, data);
1004 }
1005 else
1006 {
1007 const xmlChar *value = xmlNodeListGetString(doc, node->children, 1);
1008 _lrop(dev, doc, imgid, node->name, value, NULL, data);
1009 }
1010 }
1011
1012 xmlXPathFreeObject(xpathObj);
1013 }
1014}
1015
1016static inline void flip(float *x, float *y)
1017{
1018 const float tmp = *x;
1019 *x = 1.0 - *y;
1020 *y = 1.0 - tmp;
1021}
1022
1023static inline void swap(float *x, float *y)
1024{
1025 const float tmp = *x;
1026 *x = *y;
1027 *y = tmp;
1028}
1029
1030static inline double rotate_x(double x, double y, const double rangle)
1031{
1032 return x*cos(rangle) + y*sin(rangle);
1033}
1034
1035static inline double rotate_y(double x, double y, const double rangle)
1036{
1037 return -x*sin(rangle) + y*cos(rangle);
1038}
1039
1040static inline void rotate_xy(double *cx, double *cy, const double rangle)
1041{
1042 const double x = *cx;
1043 const double y = *cy;
1044 *cx = rotate_x(x, y, rangle);
1045 *cy = rotate_y(x, y, rangle);
1046}
1047
1048static inline float round5(double x)
1049{
1050 return round(x * 100000.f) / 100000.f;
1051}
1052
1053gboolean dt_lightroom_import(int32_t imgid, dt_develop_t *dev, gboolean iauto)
1054{
1055 gboolean refresh_needed = FALSE;
1056 char imported[256] = { 0 };
1057 int n_import = 0; // number of iop imported
1058
1059 // Get full pathname
1060 char *pathname = dt_get_lightroom_xmp(imgid);
1061
1062 if(IS_NULL_PTR(pathname))
1063 {
1064 if(!iauto) dt_control_log(_("cannot find lightroom XMP!"));
1065 return FALSE;
1066 }
1067
1068 // Load LR xmp
1069
1070 xmlDocPtr doc;
1071 xmlNodePtr entryNode;
1072
1073 // Parse xml document
1074
1075 doc = xmlReadFile(pathname, NULL, 0);
1076
1077 if(IS_NULL_PTR(doc))
1078 {
1079 dt_free(pathname);
1080 return FALSE ;
1081 }
1082
1083 // Enter first node, xmpmeta
1084
1085 entryNode = xmlDocGetRootElement(doc);
1086
1087 if(IS_NULL_PTR(entryNode))
1088 {
1089 dt_free(pathname);
1090 xmlFreeDoc(doc);
1091 return FALSE;
1092 }
1093
1094 if(xmlStrcmp(entryNode->name, (const xmlChar *)"xmpmeta"))
1095 {
1096 if(!iauto) dt_control_log(_("`%s' not a lightroom XMP!"), pathname);
1097 dt_free(pathname);
1098 return FALSE;
1099 }
1100
1101 // Check that this is really a Lightroom document
1102
1103 xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc);
1104
1105 if(IS_NULL_PTR(xpathCtx))
1106 {
1107 dt_free(pathname);
1108 xmlFreeDoc(doc);
1109 return FALSE;
1110 }
1111
1112 xmlXPathRegisterNs(xpathCtx, BAD_CAST "stEvt", BAD_CAST "http://ns.adobe.com/xap/1.0/sType/ResourceEvent#");
1113
1114 xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression((const xmlChar *)"//@stEvt:softwareAgent", xpathCtx);
1115
1116 if(IS_NULL_PTR(xpathObj))
1117 {
1118 if(!iauto) dt_control_log(_("`%s' not a lightroom XMP!"), pathname);
1119 xmlXPathFreeContext(xpathCtx);
1120 dt_free(pathname);
1121 xmlFreeDoc(doc);
1122 return FALSE;
1123 }
1124
1125 xmlNodeSetPtr xnodes = xpathObj->nodesetval;
1126
1127 if(!IS_NULL_PTR(xnodes) && xnodes->nodeNr > 0)
1128 {
1129 xmlNodePtr xnode = xnodes->nodeTab[0];
1130 xmlChar *value = xmlNodeListGetString(doc, xnode->xmlChildrenNode, 1);
1131
1132 if(!strstr((char *)value, "Lightroom") && !strstr((char *)value, "Camera Raw"))
1133 {
1134 xmlXPathFreeContext(xpathCtx);
1135 xmlXPathFreeObject(xpathObj);
1136 xmlFreeDoc(doc);
1137 xmlFree(value);
1138 if(!iauto) dt_control_log(_("`%s' not a lightroom XMP!"), pathname);
1139 dt_free(pathname);
1140 return FALSE;
1141 }
1142 xmlFree(value);
1143 }
1144// we could bail out here if we ONLY wanted to load a file known to be from lightroom.
1145// if we don't know who created it we will just import it however.
1146// else
1147// {
1148// xmlXPathFreeObject(xpathObj);
1149// xmlXPathFreeContext(xpathCtx);
1150// if(!iauto) dt_control_log(_("`%s' not a lightroom XMP!"), pathname);
1151// g_free(pathname);
1152// return;
1153// }
1154
1155 // let's now parse the needed data
1156
1157 lr_data_t data;
1158
1159 memset(&data, 0, sizeof(data));
1160
1161 data.has_crop = FALSE;
1162 data.has_flip = FALSE;
1163 data.has_exposure = FALSE;
1164 data.has_vignette = FALSE;
1165 data.has_grain = FALSE;
1166 data.has_spots = FALSE;
1167 data.curve_kind = linear;
1168 data.n_pts = 0;
1169 data.has_colorzones = FALSE;
1170 data.has_splittoning = FALSE;
1171 data.has_bilat = FALSE;
1172 data.has_tags = FALSE;
1173 data.rating = 0;
1174 data.has_rating = FALSE;
1175 data.lat = NAN;
1176 data.lon = NAN;
1177 data.has_gps = FALSE;
1178 data.color = 0;
1179 data.has_colorlabel = FALSE;
1180 data.fratio = NAN; // factor ratio image
1181 data.crop_roundness = NAN; // from lightroom
1182 data.iwidth = 0;
1183 data.iheight = 0; // image width / height
1185
1186 // record the name-spaces needed for the parsing
1187 xmlXPathRegisterNs
1188 (xpathCtx,
1189 BAD_CAST "crs",
1190 BAD_CAST "http://ns.adobe.com/camera-raw-settings/1.0/");
1191 xmlXPathRegisterNs
1192 (xpathCtx,
1193 BAD_CAST "dc",
1194 BAD_CAST "http://purl.org/dc/elements/1.1/");
1195 xmlXPathRegisterNs
1196 (xpathCtx,
1197 BAD_CAST "tiff",
1198 BAD_CAST "http://ns.adobe.com/tiff/1.0/");
1199 xmlXPathRegisterNs
1200 (xpathCtx,
1201 BAD_CAST "xmp",
1202 BAD_CAST "http://ns.adobe.com/xap/1.0/");
1203 xmlXPathRegisterNs
1204 (xpathCtx,
1205 BAD_CAST "exif",
1206 BAD_CAST "http://ns.adobe.com/exif/1.0/");
1207 xmlXPathRegisterNs
1208 (xpathCtx,
1209 BAD_CAST "lr",
1210 BAD_CAST "http://ns.adobe.com/lightroom/1.0/");
1211 xmlXPathRegisterNs
1212 (xpathCtx,
1213 BAD_CAST "rdf",
1214 BAD_CAST "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
1215
1216 // All prefixes to parse from the XMP document
1217 static char *names[] = { "crs", "dc", "tiff", "xmp", "exif", "lr", NULL };
1218
1219 for (int i=0; names[i]!=NULL; i++)
1220 {
1221 char expr[50];
1222
1223 /* Lr 7.0 CC (nodes) */
1224 snprintf(expr, sizeof(expr), "//%s:*", names[i]);
1225 _handle_xpath(dev, doc, imgid, xpathCtx, (const xmlChar *)expr, &data);
1226
1227 /* Lr up to 6.0 (attributes) */
1228 snprintf(expr, sizeof(expr), "//@%s:*", names[i]);
1229 _handle_xpath(dev, doc, imgid, xpathCtx, (const xmlChar *)expr, &data);
1230 }
1231
1232 xmlXPathFreeObject(xpathObj);
1233 xmlXPathFreeContext(xpathCtx);
1234 xmlFreeDoc(doc);
1235
1236 // Integrates into the history all the imported iop
1237
1238 if(!IS_NULL_PTR(dev) && dt_image_is_raw(&dev->image_storage))
1239 {
1240 // set colorin to cmatrix which is the default from Adobe (so closer to what Lightroom does)
1242
1243 dt_add_hist(imgid, "colorin", (dt_iop_params_t *)&pci, sizeof(dt_iop_colorin_params_v1_t), imported,
1244 sizeof(imported), LRDT_COLORIN_VERSION, &n_import);
1245 refresh_needed = TRUE;
1246 }
1247
1248 if(!IS_NULL_PTR(dev) && data.has_crop)
1249 {
1250 double rangle;
1251 double cx, cw, cy, ch;
1252 double new_width, new_height;
1254
1255 data.pc.k_sym = 0;
1256 data.pc.k_apply = 0;
1257 data.pc.crop_auto = 0; // Cannot use crop-auto=1 (the default at clipping GUI), as it does not allow to cover all cropping cases.
1258 data.pc.ratio_n = data.pc.ratio_d = -2;
1259 data.pc.k_h = data.pc.k_v = 0;
1260 data.pc.k_type = 0;
1261 data.pc.kxa = data.pc.kxd = 0.2f;
1262 data.pc.kxc = data.pc.kxb = 0.8f;
1263 data.pc.kya = data.pc.kyb = 0.2f;
1264 data.pc.kyc = data.pc.kyd = 0.8f;
1265
1266 // Convert image in image-centered coordinate system, [-image_size / 2; + image_size / 2]
1267 cx = (data.pc.cx - 0.5f) * data.iwidth;
1268 cw = (data.pc.cw - 0.5f) * data.iwidth;
1269 cy = (data.pc.cy - 0.5f) * data.iheight;
1270 ch = (data.pc.ch - 0.5f) * data.iheight;
1271
1272 // Rotate the cropped zone according to rotation angle
1273 // All rotations done using center of the image
1274 rangle = data.pc.angle * (M_PI / 180.0f);
1275 rotate_xy(&cx, &cy, -rangle);
1276 rotate_xy(&cw, &ch, -rangle);
1277
1278 // Calculate the new overall image size (black zone included) after rotation
1279 // rangle is limited to -45°;+45° by LR
1280 new_width = rotate_x(+data.iwidth, -data.iheight, -fabs(rangle));
1281 new_height = rotate_y(+data.iwidth, +data.iheight, -fabs(rangle));
1282
1283 // apply new size & convert image back in initial coordinate system [0.0 ; +1.0]
1284 data.pc.cx = round5((cx / new_width) + 0.5f);
1285 data.pc.cw = round5((cw / new_width) + 0.5f);
1286 data.pc.cy = round5((cy / new_height) + 0.5f);
1287 data.pc.ch = round5((ch / new_height) + 0.5f);
1288
1289 // adjust crop data according to the orientation - Must be done after rotation
1290 if(orientation & ORIENTATION_FLIP_X)
1291 flip(&data.pc.cx, &data.pc.cw);
1292 if(orientation & ORIENTATION_FLIP_Y)
1293 flip(&data.pc.cy, &data.pc.ch);
1294 if(orientation & ORIENTATION_SWAP_XY)
1295 {
1296 swap(&data.pc.cx, &data.pc.cy);
1297 swap(&data.pc.cw, &data.pc.ch);
1298 }
1299
1300 // Invert angle when orientation is flipped
1301 if(orientation == ORIENTATION_FLIP_HORIZONTALLY
1302 || orientation == ORIENTATION_FLIP_VERTICALLY
1303 || orientation == ORIENTATION_TRANSPOSE
1304 || orientation == ORIENTATION_TRANSVERSE)
1305 data.pc.angle = -data.pc.angle;
1306
1307 data.fratio = (data.pc.cw - data.pc.cx) / (data.pc.ch - data.pc.cy);
1308
1309 dt_add_hist(imgid, "clipping", (dt_iop_params_t *)&data.pc, sizeof(dt_iop_clipping_params_t), imported,
1310 sizeof(imported), LRDT_CLIPPING_VERSION, &n_import);
1311 refresh_needed = TRUE;
1312 }
1313
1314 if(!IS_NULL_PTR(dev) && data.has_flip)
1315 {
1317
1318 dt_add_hist(imgid, "flip", (dt_iop_params_t *)&data.pf, sizeof(dt_iop_flip_params_t), imported,
1319 sizeof(imported), LRDT_FLIP_VERSION, &n_import);
1320 refresh_needed = TRUE;
1321 }
1322
1323 if(!IS_NULL_PTR(dev) && data.has_exposure)
1324 {
1325 dt_add_hist(imgid, "exposure", (dt_iop_params_t *)&data.pe, sizeof(dt_iop_exposure_params_t), imported,
1326 sizeof(imported), LRDT_EXPOSURE_VERSION, &n_import);
1327 refresh_needed = TRUE;
1328 }
1329
1330 if(!IS_NULL_PTR(dev) && data.has_grain)
1331 {
1332 data.pg.channel = 0;
1333
1334 dt_add_hist(imgid, "grain", (dt_iop_params_t *)&data.pg, sizeof(dt_iop_grain_params_t), imported,
1335 sizeof(imported), LRDT_GRAIN_VERSION, &n_import);
1336 refresh_needed = TRUE;
1337 }
1338
1339 if(!IS_NULL_PTR(dev) && data.has_vignette)
1340 {
1341 const float base_ratio = 1.325 / 1.5;
1342
1343 data.pv.autoratio = FALSE;
1344 data.pv.dithering = DITHER_8BIT;
1345 data.pv.center.x = 0.0;
1346 data.pv.center.y = 0.0;
1347 data.pv.shape = 1.0;
1348
1349 // defensive code, should not happen, but just in case future Lr version
1350 // has not ImageWidth/ImageLength XML tag.
1351 if(data.iwidth == 0 || data.iheight == 0)
1352 data.pv.whratio = base_ratio;
1353 else
1354 data.pv.whratio = base_ratio * ((float)data.iwidth / (float)data.iheight);
1355
1356 if(data.has_crop) data.pv.whratio = data.pv.whratio * data.fratio;
1357
1358 // Adjust scale and ratio based on the roundness. On Lightroom changing
1359 // the roundness change the width and the height of the vignette.
1360
1361 if(data.crop_roundness > 0)
1362 {
1363 float newratio = data.pv.whratio - (data.pv.whratio - 1) * (data.crop_roundness / 100.0);
1364 float dscale = (1 - (newratio / data.pv.whratio)) / 2.0;
1365
1366 data.pv.scale -= dscale * 100.0;
1367 data.pv.whratio = newratio;
1368 }
1369
1370 dt_add_hist(imgid, "vignette", (dt_iop_params_t *)&data.pv, sizeof(dt_iop_vignette_params_t), imported,
1371 sizeof(imported), LRDT_VIGNETTE_VERSION, &n_import);
1372 refresh_needed = TRUE;
1373 }
1374
1375 if(!IS_NULL_PTR(dev) && data.has_spots)
1376 {
1377 // Check for orientation, rotate when in portrait mode
1378 if(data.orientation > 4)
1379 for(int k = 0; k < data.ps.num_spots; k++)
1380 {
1381 float tmp = data.ps.spot[k].y;
1382 data.ps.spot[k].y = 1.0 - data.ps.spot[k].x;
1383 data.ps.spot[k].x = tmp;
1384 tmp = data.ps.spot[k].yc;
1385 data.ps.spot[k].yc = 1.0 - data.ps.spot[k].xc;
1386 data.ps.spot[k].xc = tmp;
1387 }
1388
1389 dt_add_hist(imgid, "spots", (dt_iop_params_t *)&data.ps, sizeof(dt_iop_spots_params_t), imported,
1390 sizeof(imported), LRDT_SPOTS_VERSION, &n_import);
1391 refresh_needed = TRUE;
1392 }
1393
1394 if(!IS_NULL_PTR(dev) &&
1395 (data.curve_kind != linear
1396 || data.ptc_value[0] != 0 || data.ptc_value[1] != 0 || data.ptc_value[2] != 0 || data.ptc_value[3] != 0))
1397 {
1398 const int total_pts = (data.curve_kind == custom) ? data.n_pts : 6;
1399 data.ptc.tonecurve_nodes[ch_L] = total_pts;
1400 data.ptc.tonecurve_nodes[ch_a] = 7;
1401 data.ptc.tonecurve_nodes[ch_b] = 7;
1405 data.ptc.tonecurve_autoscale_ab = 1;
1406 data.ptc.tonecurve_preset = 0;
1407
1408 float linear_ab[7] = { 0.0, 0.08, 0.3, 0.5, 0.7, 0.92, 1.0 };
1409
1410 // linear a, b curves
1411 for(int k = 0; k < 7; k++) data.ptc.tonecurve[ch_a][k].x = linear_ab[k];
1412 for(int k = 0; k < 7; k++) data.ptc.tonecurve[ch_a][k].y = linear_ab[k];
1413 for(int k = 0; k < 7; k++) data.ptc.tonecurve[ch_b][k].x = linear_ab[k];
1414 for(int k = 0; k < 7; k++) data.ptc.tonecurve[ch_b][k].y = linear_ab[k];
1415
1416 // Set the base tonecurve
1417
1418 if(data.curve_kind == linear)
1419 {
1420 data.ptc.tonecurve[ch_L][0].x = 0.0;
1421 data.ptc.tonecurve[ch_L][0].y = 0.0;
1422 data.ptc.tonecurve[ch_L][1].x = data.ptc_split[0] / 2.0;
1423 data.ptc.tonecurve[ch_L][1].y = data.ptc_split[0] / 2.0;
1424 data.ptc.tonecurve[ch_L][2].x = data.ptc_split[1] - (data.ptc_split[1] - data.ptc_split[0]) / 2.0;
1425 data.ptc.tonecurve[ch_L][2].y = data.ptc_split[1] - (data.ptc_split[1] - data.ptc_split[0]) / 2.0;
1426 data.ptc.tonecurve[ch_L][3].x = data.ptc_split[1] + (data.ptc_split[2] - data.ptc_split[1]) / 2.0;
1427 data.ptc.tonecurve[ch_L][3].y = data.ptc_split[1] + (data.ptc_split[2] - data.ptc_split[1]) / 2.0;
1428 data.ptc.tonecurve[ch_L][4].x = data.ptc_split[2] + (1.0 - data.ptc_split[2]) / 2.0;
1429 data.ptc.tonecurve[ch_L][4].y = data.ptc_split[2] + (1.0 - data.ptc_split[2]) / 2.0;
1430 data.ptc.tonecurve[ch_L][5].x = 1.0;
1431 data.ptc.tonecurve[ch_L][5].y = 1.0;
1432 }
1433 else
1434 {
1435 for(int k = 0; k < total_pts; k++)
1436 {
1437 data.ptc.tonecurve[ch_L][k].x = data.curve_pts[k][0] / 255.0;
1438 data.ptc.tonecurve[ch_L][k].y = data.curve_pts[k][1] / 255.0;
1439 }
1440 }
1441
1442 if(data.curve_kind != custom)
1443 {
1444 // set shadows/darks/lights/highlight adjustments
1445
1446 data.ptc.tonecurve[ch_L][1].y += data.ptc.tonecurve[ch_L][1].y * ((float)data.ptc_value[0] / 100.0);
1447 data.ptc.tonecurve[ch_L][2].y += data.ptc.tonecurve[ch_L][2].y * ((float)data.ptc_value[1] / 100.0);
1448 data.ptc.tonecurve[ch_L][3].y += data.ptc.tonecurve[ch_L][3].y * ((float)data.ptc_value[2] / 100.0);
1449 data.ptc.tonecurve[ch_L][4].y += data.ptc.tonecurve[ch_L][4].y * ((float)data.ptc_value[3] / 100.0);
1450
1451 if(data.ptc.tonecurve[ch_L][1].y > data.ptc.tonecurve[ch_L][2].y)
1452 data.ptc.tonecurve[ch_L][1].y = data.ptc.tonecurve[ch_L][2].y;
1453 if(data.ptc.tonecurve[ch_L][3].y > data.ptc.tonecurve[ch_L][4].y)
1454 data.ptc.tonecurve[ch_L][4].y = data.ptc.tonecurve[ch_L][3].y;
1455 }
1456
1457 dt_add_hist(imgid, "tonecurve", (dt_iop_params_t *)&data.ptc, sizeof(dt_iop_tonecurve_params_t), imported,
1458 sizeof(imported), LRDT_TONECURVE_VERSION, &n_import);
1459 refresh_needed = TRUE;
1460 }
1461
1462 if(!IS_NULL_PTR(dev) && data.has_colorzones)
1463 {
1465
1466 for(int i = 0; i < 3; i++)
1467 for(int k = 0; k < 8; k++)
1468 data.pcz.equalizer_x[i][k] = k / (DT_IOP_COLORZONES_BANDS - 1.0);
1469
1470 dt_add_hist(imgid, "colorzones", (dt_iop_params_t *)&data.pcz, sizeof(dt_iop_colorzones_params_t), imported,
1471 sizeof(imported), LRDT_COLORZONES_VERSION, &n_import);
1472 refresh_needed = TRUE;
1473 }
1474
1475 if(!IS_NULL_PTR(dev) && data.has_splittoning)
1476 {
1477 data.pst.compress = 50.0;
1478
1479 dt_add_hist(imgid, "splittoning", (dt_iop_params_t *)&data.pst, sizeof(dt_iop_splittoning_params_t), imported,
1480 sizeof(imported), LRDT_SPLITTONING_VERSION, &n_import);
1481 refresh_needed = TRUE;
1482 }
1483
1484 if(!IS_NULL_PTR(dev) && data.has_bilat)
1485 {
1486 data.pbl.sigma_r = 100.0;
1487 data.pbl.sigma_s = 100.0;
1488
1489 dt_add_hist(imgid, "bilat", (dt_iop_params_t *)&data.pbl, sizeof(dt_iop_bilat_params_t), imported,
1490 sizeof(imported), LRDT_BILAT_VERSION, &n_import);
1491 refresh_needed = TRUE;
1492 }
1493
1494 if(data.has_tags)
1495 {
1496 if(imported[0]) g_strlcat(imported, ", ", sizeof(imported));
1497 g_strlcat(imported, _("tags"), sizeof(imported));
1498 n_import++;
1499 }
1500
1501 if(IS_NULL_PTR(dev) && data.has_rating)
1502 {
1504
1505 if(imported[0]) g_strlcat(imported, ", ", sizeof(imported));
1506 g_strlcat(imported, _("rating"), sizeof(imported));
1507 n_import++;
1508 }
1509
1510 if(IS_NULL_PTR(dev) && data.has_gps)
1511 {
1512 dt_image_geoloc_t geoloc;
1513 geoloc.longitude = data.lon;
1514 geoloc.latitude = data.lat;
1515 geoloc.elevation = NAN;
1516 dt_image_set_location(imgid, &geoloc, FALSE, FALSE);
1517 GList *imgs = NULL;
1518 imgs = g_list_prepend(imgs, GINT_TO_POINTER(imgid));
1520
1521 if(imported[0]) g_strlcat(imported, ", ", sizeof(imported));
1522 g_strlcat(imported, _("geotagging"), sizeof(imported));
1523 n_import++;
1524 }
1525
1526 if(IS_NULL_PTR(dev) && data.has_colorlabel)
1527 {
1528 dt_colorlabels_set_label(imgid, data.color);
1529
1530 if(imported[0]) g_strlcat(imported, ", ", sizeof(imported));
1531 g_strlcat(imported, _("color label"), sizeof(imported));
1532 n_import++;
1533 }
1534
1535 if(!IS_NULL_PTR(dev) && refresh_needed && dev->gui_attached)
1536 {
1537 dt_control_log(ngettext("%s has been imported", "%s have been imported", n_import), imported);
1538
1539 if(!iauto)
1540 {
1541 /* signal history changed */
1543 dt_dev_reload_history_items(dev, imgid);
1546 dt_dev_history_notify_change(dev, imgid);
1547 /* update xmp file */
1548 dt_image_synch_xmp(imgid);
1550 }
1551 }
1552 return TRUE;
1553}
1554// clang-format off
1555// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1556// vim: shiftwidth=2 expandtab tabstop=2 cindent
1557// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1558// clang-format on
#define DT_IOP_TONECURVE_MAXNODES
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
size_t params_size(dt_imageio_module_format_t *self)
Definition avif.c:571
void dt_colorlabels_set_label(const int32_t imgid, const int color)
static const float x
const float v
#define DT_IOP_COLORZONES_BANDS
Definition colorzones.c:101
gboolean dt_image_is_raw(const dt_image_t *img)
void dt_image_set_location(const int32_t imgid, const dt_image_geoloc_t *geoloc, const gboolean undo_on, const gboolean group_on)
void dt_image_synch_xmp(const int selected)
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.
void dt_control_log(const char *msg,...)
Definition control.c:824
#define CUBIC_SPLINE
Definition curve_tools.h:34
void dt_dev_write_history(dt_develop_t *dev, gboolean async)
Thread-safe wrapper around dt_dev_write_history_ext() for dev->image_storage.id.
void dt_dev_history_pixelpipe_update(dt_develop_t *dev, gboolean rebuild)
Rebuild or resync pixelpipes after backend history changes.
void dt_dev_history_notify_change(dt_develop_t *dev, const int32_t imgid)
Notify the rest of the app that history changes were written.
gboolean dt_dev_reload_history_items(dt_develop_t *dev, const int32_t imgid)
Reload history from DB and rebuild pipelines/GUI state.
void dt_dev_history_gui_update(struct dt_develop_t *dev)
Apply history-loaded params to module GUIs.
void dt_iop_params_t
Definition dev_history.h:43
GHashTable * names
GtkWidget* -> the name the application knows it by.
_dt_iop_grain_channel_t
Definition grain.c:87
gboolean dt_history_repository_write_item(const int32_t imgid, const int num, const char *operation, const void *op_params, const int op_params_size, const int module_version, const gboolean enabled, const void *blendop_params, const int blendop_params_size, const int blendop_version, const int multi_priority, const char *multi_name)
gboolean dt_history_repository_set_end(const int32_t imgid, const int32_t history_end)
int32_t dt_history_repository_get_next_num(const int32_t imgid)
static dt_image_orientation_t dt_image_orientation_to_flip_bits(const int orient)
Definition image.h:702
dt_image_orientation_t
Definition image.h:216
@ ORIENTATION_TRANSVERSE
Definition image.h:230
@ ORIENTATION_SWAP_XY
Definition image.h:221
@ ORIENTATION_FLIP_Y
Definition image.h:219
@ ORIENTATION_ROTATE_CCW_90_DEG
Definition image.h:228
@ ORIENTATION_ROTATE_CW_90_DEG
Definition image.h:229
@ ORIENTATION_NONE
Definition image.h:218
@ ORIENTATION_FLIP_X
Definition image.h:220
@ ORIENTATION_TRANSPOSE
Definition image.h:227
@ ORIENTATION_FLIP_VERTICALLY
Definition image.h:225
@ ORIENTATION_FLIP_HORIZONTALLY
Definition image.h:224
dt_exif_image_orientation_t
Definition image.h:204
@ EXIF_ORIENTATION_NONE
Definition image.h:205
@ EXIF_ORIENTATION_ROTATE_CW_90_DEG
Definition image.h:211
@ EXIF_ORIENTATION_ROTATE_CCW_90_DEG
Definition image.h:210
const gchar * dt_iop_get_localized_name(const gchar *op)
Definition imageop.c:1757
static gboolean _skip_key_value_pair(const char **startptr, const char *key)
Definition lightroom.c:444
struct dt_iop_tonecurve_params_t dt_iop_tonecurve_params_t
static void _handle_xpath(dt_develop_t *dev, xmlDoc *doc, int32_t imgid, xmlXPathContext *ctx, const xmlChar *xpath, lr_data_t *data)
Definition lightroom.c:984
static gboolean _skip_comma(const char **startptr)
Definition lightroom.c:460
static void rotate_xy(double *cx, double *cy, const double rangle)
Definition lightroom.c:1040
static gboolean _read_float(const char **startptr, const char *key, float *value)
Definition lightroom.c:429
_dt_iop_grain_channel_t
Definition lightroom.c:95
@ DT_GRAIN_CHANNEL_RGB
Definition lightroom.c:99
@ DT_GRAIN_CHANNEL_HUE
Definition lightroom.c:96
@ DT_GRAIN_CHANNEL_SATURATION
Definition lightroom.c:97
@ DT_GRAIN_CHANNEL_LIGHTNESS
Definition lightroom.c:98
#define LRDT_BLEND_VERSION
Definition lightroom.c:235
#define LRDT_SPOTS_VERSION
Definition lightroom.c:136
dt_iop_dither_t
Definition lightroom.c:110
@ DITHER_16BIT
Definition lightroom.c:113
@ DITHER_8BIT
Definition lightroom.c:112
@ DITHER_OFF
Definition lightroom.c:111
struct dt_iop_exposure_params_t dt_iop_exposure_params_t
#define LRDT_VIGNETTE_VERSION
Definition lightroom.c:122
#define MAX_SPOTS
Definition lightroom.c:137
struct dt_iop_vignette_params_t dt_iop_vignette_params_t
struct dt_iop_splittoning_params_t dt_iop_splittoning_params_t
static float lr2dt_vignette_gain(float value)
Definition lightroom.c:308
struct lr2dt lr2dt_t
struct dt_iop_bilat_params_t dt_iop_bilat_params_t
static float get_interpolate(lr2dt_t lr2dt_table[], float value)
Definition lightroom.c:289
static void swap(float *x, float *y)
Definition lightroom.c:1023
static void _lrop(const dt_develop_t *dev, const xmlDocPtr doc, const int32_t imgid, const xmlChar *name, const xmlChar *value, const xmlNodePtr node, lr_data_t *data)
Definition lightroom.c:466
#define DT_IOP_COLOR_ICC_LEN_V1
Definition lightroom.c:217
static double rotate_y(double x, double y, const double rangle)
Definition lightroom.c:1035
static float lr2dt_splittoning_balance(float value)
Definition lightroom.c:336
struct dt_iop_colorzones_params_t dt_iop_colorzones_params_t
static float lr2dt_clarity(float value)
Definition lightroom.c:343
static float lr2dt_grain_frequency(float value)
Definition lightroom.c:329
#define LRDT_FLIP_VERSION
Definition lightroom.c:81
#define LRDT_CLIPPING_VERSION
Definition lightroom.c:71
static float round5(double x)
Definition lightroom.c:1048
gboolean dt_lightroom_import(int32_t imgid, dt_develop_t *dev, gboolean iauto)
Definition lightroom.c:1053
dt_iop_colorzones_channel_t
Definition lightroom.c:184
@ DT_IOP_COLORZONES_L
Definition lightroom.c:185
@ DT_IOP_COLORZONES_h
Definition lightroom.c:187
@ DT_IOP_COLORZONES_C
Definition lightroom.c:186
lr_curve_kind_t
Definition lightroom.c:368
@ strong_contrast
Definition lightroom.c:371
@ linear
Definition lightroom.c:369
@ custom
Definition lightroom.c:372
@ medium_contrast
Definition lightroom.c:370
#define DT_IOP_COLORZONES_BANDS
Definition lightroom.c:181
#define LRDT_EXPOSURE_VERSION
Definition lightroom.c:87
static void dt_add_hist(int32_t imgid, char *operation, dt_iop_params_t *params, int params_size, char *imported, size_t imported_len, int version, int *import_count)
Definition lightroom.c:350
#define LRDT_COLORIN_VERSION
Definition lightroom.c:216
#define LRDT_TONECURVE_VERSION
Definition lightroom.c:154
struct dt_iop_spots_params_t dt_iop_spots_params_t
struct dt_iop_grain_params_t dt_iop_grain_params_t
#define LRDT_GRAIN_VERSION
Definition lightroom.c:93
static float lr2dt_grain_amount(float value)
Definition lightroom.c:322
struct dt_iop_tonecurve_node_t dt_iop_tonecurve_node_t
#define LRDT_COLORZONES_VERSION
Definition lightroom.c:180
struct dt_iop_fvector_2d_t dt_iop_vector_2d_t
static int _has_list(char *name)
Definition lightroom.c:970
static float lr2dt_vignette_midpoint(float value)
Definition lightroom.c:315
#define LRDT_SPLITTONING_VERSION
Definition lightroom.c:196
#define LRDT_BILAT_VERSION
Definition lightroom.c:207
#define DEVELOP_BLENDIF_SIZE
Definition lightroom.c:236
struct dt_iop_flip_params_t dt_iop_flip_params_t
static double rotate_x(double x, double y, const double rangle)
Definition lightroom.c:1030
struct dt_iop_clipping_params_t dt_iop_clipping_params_t
static float lr2dt_blacks(float value)
Definition lightroom.c:300
#define MAX_PTS
Definition lightroom.c:365
tonecurve_channel_t
Definition lightroom.c:157
@ ch_L
Definition lightroom.c:158
@ ch_b
Definition lightroom.c:160
@ ch_a
Definition lightroom.c:159
@ ch_max
Definition lightroom.c:161
char * dt_get_lightroom_xmp(int32_t imgid)
Definition lightroom.c:263
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(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
char * key
void dt_metadata_set_import(const int32_t imgid, const char *key, const char *value)
#define DT_MAX_FILENAME_LEN
Definition paths.h:35
const char * name
Definition pdf.h:90
The colour-profile vocabulary, and nothing else.
dt_iop_color_intent_t
ICC rendering intent, as stored in iop params and in conf.
@ DT_INTENT_PERCEPTUAL
void dt_ratings_apply_on_image(const int32_t imgid, const int rating, const gboolean single_star_toggle, const gboolean undo_on, const gboolean group_on)
Definition ratings.c:216
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:386
struct dt_control_signal_t * dt_control_signal_get_global(void)
Definition darktable.c:616
@ DT_SIGNAL_DEVELOP_HISTORY_CHANGE
This signal is raised when develop history is changed no param, no returned value.
Definition signal.h:207
@ DT_SIGNAL_GEOTAG_CHANGED
This signal is raised when a geotag is added/deleted/changed
Definition signal.h:139
@ DT_SIGNAL_TAG_CHANGED
This signal is raised when a tag is added/deleted/changed
Definition signal.h:133
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
const float const int flip
float blendif_parameters[4 *DEVELOP_BLENDIF_SIZE]
Definition blend.h:231
int32_t gui_attached
Definition develop.h:167
dt_image_t image_storage
Definition develop.h:225
double latitude
Definition image.h:332
double elevation
Definition image.h:332
double longitude
Definition image.h:332
dt_image_orientation_t orientation
Definition image.h:366
dt_iop_color_intent_t intent
Definition lightroom.c:222
dt_image_orientation_t orientation
Definition lightroom.c:84
_dt_iop_grain_channel_t channel
Definition lightroom.c:104
dt_iop_tonecurve_node_t tonecurve[3][20]
Definition lightroom.c:172
dt_iop_vector_2d_t center
Definition lightroom.c:129
float dt
Definition lightroom.c:260
float lr
Definition lightroom.c:260
gboolean has_flip
Definition lightroom.c:381
gboolean has_colorlabel
Definition lightroom.c:420
dt_iop_tonecurve_params_t ptc
Definition lightroom.c:395
float fratio
Definition lightroom.c:422
gboolean has_crop
Definition lightroom.c:378
gdouble lat
Definition lightroom.c:416
gboolean has_vignette
Definition lightroom.c:387
dt_iop_colorzones_params_t pcz
Definition lightroom.c:402
gboolean has_bilat
Definition lightroom.c:409
dt_iop_splittoning_params_t pst
Definition lightroom.c:405
dt_iop_spots_params_t ps
Definition lightroom.c:392
float ptc_split[3]
Definition lightroom.c:397
dt_iop_flip_params_t pf
Definition lightroom.c:380
gboolean has_spots
Definition lightroom.c:393
dt_iop_clipping_params_t pc
Definition lightroom.c:377
int curve_pts[20][2]
Definition lightroom.c:399
int iheight
Definition lightroom.c:424
lr_curve_kind_t curve_kind
Definition lightroom.c:398
gdouble lon
Definition lightroom.c:416
dt_iop_grain_params_t pg
Definition lightroom.c:389
gboolean has_gps
Definition lightroom.c:417
gboolean has_rating
Definition lightroom.c:414
dt_iop_bilat_params_t pbl
Definition lightroom.c:408
dt_iop_exposure_params_t pe
Definition lightroom.c:383
float crop_roundness
Definition lightroom.c:423
gboolean has_tags
Definition lightroom.c:411
gboolean has_grain
Definition lightroom.c:390
int ptc_value[4]
Definition lightroom.c:396
gboolean has_splittoning
Definition lightroom.c:406
gboolean has_exposure
Definition lightroom.c:384
dt_exif_image_orientation_t orientation
Definition lightroom.c:425
gboolean has_colorzones
Definition lightroom.c:403
dt_iop_vignette_params_t pv
Definition lightroom.c:386
float yc
Definition lightroom.c:144
float x
Definition lightroom.c:142
float y
Definition lightroom.c:142
float radius
Definition lightroom.c:145
float xc
Definition lightroom.c:144
gboolean dt_tag_attach(const guint tagid, const int32_t imgid, const gboolean undo_on, const gboolean group_on)
Definition tags.c:377
gboolean dt_tag_new(const char *name, guint *tagid)
Definition tags.c:164
gboolean dt_tag_exists(const char *name, guint *tagid)
Definition tags.c:255
Telling the user something happened.
double dt_util_gps_string_to_number(const gchar *input)
Definition utility.c:609