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