Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
imageio_libraw.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2021 Daniel Vogelbacher.
4 Copyright (C) 2021-2022 Miloš Komarčević.
5 Copyright (C) 2021-2022 Pascal Obry.
6 Copyright (C) 2022 Martin Bařinka.
7 Copyright (C) 2022 Philipp Lutz.
8 Copyright (C) 2023 Aurélien PIERRE.
9
10 darktable is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 darktable is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with darktable. If not, see <http://www.gnu.org/licenses/>.
22*/
23
24#ifdef HAVE_LIBRAW
25#include "system/macros.h"
26#include "system/mem_alloc.h"
28#include "develop/develop.h"
29#include "metadata/exif.h"
30
31#include <memory.h>
32#include <stdio.h>
33#include <inttypes.h>
34#include <strings.h>
35#include <assert.h>
36
37#include <libraw/libraw.h>
38
39
40typedef struct model_map
41{
42 const gchar *exif_make;
43 const gchar *exif_model;
44 const gchar *clean_make;
45 const gchar *clean_model;
46 const gchar *clean_alias;
47
48} model_map_t;
49
50
51const model_map_t modelMap[] = {
52 {
53 .exif_make = "Canon",
54 .exif_model = "Canon EOS R",
55 .clean_make = "Canon",
56 .clean_model = "EOS R",
57 .clean_alias = "EOS R"
58 },
59 {
60 .exif_make = "Canon",
61 .exif_model = "Canon EOS RP",
62 .clean_make = "Canon",
63 .clean_model = "EOS RP",
64 .clean_alias = "EOS RP"
65 },
66 {
67 .exif_make = "Canon",
68 .exif_model = "Canon EOS R5",
69 .clean_make = "Canon",
70 .clean_model = "EOS R5",
71 .clean_alias = "EOS R5"
72 },
73 {
74 .exif_make = "Canon",
75 .exif_model = "Canon EOS R6",
76 .clean_make = "Canon",
77 .clean_model = "EOS R6",
78 .clean_alias = "EOS R6"
79 },
80 {
81 .exif_make = "Canon",
82 .exif_model = "Canon EOS R3",
83 .clean_make = "Canon",
84 .clean_model = "EOS R3",
85 .clean_alias = "EOS R3"
86 },
87 {
88 .exif_make = "Canon",
89 .exif_model = "Canon EOS R7",
90 .clean_make = "Canon",
91 .clean_model = "EOS R7",
92 .clean_alias = "EOS R7"
93 },
94 {
95 .exif_make = "Canon",
96 .exif_model = "Canon EOS R10",
97 .clean_make = "Canon",
98 .clean_model = "EOS R10",
99 .clean_alias = "EOS R10"
100 },
101 {
102 .exif_make = "Canon",
103 .exif_model = "Canon EOS M50",
104 .clean_make = "Canon",
105 .clean_model = "EOS M50",
106 .clean_alias = "EOS M50"
107 },
108 {
109 .exif_make = "Canon",
110 .exif_model = "Canon EOS KISS M",
111 .clean_make = "Canon",
112 .clean_model = "EOS M50",
113 .clean_alias = "EOS KISS M"
114 },
115 {
116 .exif_make = "Canon",
117 .exif_model = "Canon EOS M50m2",
118 .clean_make = "Canon",
119 .clean_model = "EOS M50 Mark II",
120 .clean_alias = "EOS M50 Mark II"
121 },
122 {
123 .exif_make = "Canon",
124 .exif_model = "Canon EOS KISS M2",
125 .clean_make = "Canon",
126 .clean_model = "EOS M50 Mark II",
127 .clean_alias = "EOS KISS M2"
128 },
129 {
130 .exif_make = "Canon",
131 .exif_model = "Canon EOS M6 Mark II",
132 .clean_make = "Canon",
133 .clean_model = "EOS M6 Mark II",
134 .clean_alias = "EOS M6 Mark II"
135 },
136 {
137 .exif_make = "Canon",
138 .exif_model = "Canon EOS M200",
139 .clean_make = "Canon",
140 .clean_model = "EOS M200",
141 .clean_alias = "EOS M200"
142 },
143 {
144 .exif_make = "Canon",
145 .exif_model = "Canon EOS 250D",
146 .clean_make = "Canon",
147 .clean_model = "EOS 250D",
148 .clean_alias = "EOS 250D"
149 },
150 {
151 .exif_make = "Canon",
152 .exif_model = "Canon EOS Kiss X10",
153 .clean_make = "Canon",
154 .clean_model = "EOS 250D",
155 .clean_alias = "EOS Kiss X10"
156 },
157 {
158 .exif_make = "Canon",
159 .exif_model = "Canon EOS Rebel SL3",
160 .clean_make = "Canon",
161 .clean_model = "EOS 250D",
162 .clean_alias = "EOS Rebel SL3"
163 },
164 {
165 .exif_make = "Canon",
166 .exif_model = "Canon EOS 200D II",
167 .clean_make = "Canon",
168 .clean_model = "EOS 250D",
169 .clean_alias = "EOS 200D Mark II"
170 },
171 {
172 .exif_make = "Canon",
173 .exif_model = "Canon EOS 850D",
174 .clean_make = "Canon",
175 .clean_model = "EOS 850D",
176 .clean_alias = "EOS 850D"
177 },
178 {
179 .exif_make = "Canon",
180 .exif_model = "Canon EOS Kiss X10i",
181 .clean_make = "Canon",
182 .clean_model = "EOS 850D",
183 .clean_alias = "EOS Kiss X10i"
184 },
185 {
186 .exif_make = "Canon",
187 .exif_model = "Canon EOS Rebel T8i",
188 .clean_make = "Canon",
189 .clean_model = "EOS 850D",
190 .clean_alias = "EOS Rebel T8i"
191 },
192 {
193 .exif_make = "Canon",
194 .exif_model = "Canon EOS 90D",
195 .clean_make = "Canon",
196 .clean_model = "EOS 90D",
197 .clean_alias = "EOS 90D"
198 },
199 {
200 .exif_make = "Canon",
201 .exif_model = "Canon EOS-1D X Mark III",
202 .clean_make = "Canon",
203 .clean_model = "EOS-1D X Mark III",
204 .clean_alias = "EOS-1D X Mark III"
205 },
206 {
207 .exif_make = "Canon",
208 .exif_model = "Canon PowerShot G7 X Mark III",
209 .clean_make = "Canon",
210 .clean_model = "PowerShot G7 X Mark III",
211 .clean_alias = "PowerShot G7 X Mark III"
212 },
213 {
214 .exif_make = "Canon",
215 .exif_model = "Canon PowerShot G5 X Mark II",
216 .clean_make = "Canon",
217 .clean_model = "PowerShot G5 X Mark II",
218 .clean_alias = "PowerShot G5 X Mark II"
219 }
220};
221
222gboolean dt_libraw_lookup_makermodel(const char *maker, const char *model,
223 char *mk, int mk_len, char *md, int md_len,
224 char *al, int al_len)
225{
226 for(int i = 0; i < sizeof(modelMap) / sizeof(modelMap[0]); ++i)
227 {
228 if(!g_strcmp0(maker, modelMap[i].exif_make) && !g_strcmp0(model, modelMap[i].exif_model))
229 {
230 //printf("input model: %s, exif model: %s\n", model, modelMap[i].exif_model);
231 g_strlcpy(mk, modelMap[i].clean_make, mk_len);
232 g_strlcpy(md, modelMap[i].clean_model, md_len);
233 g_strlcpy(al, modelMap[i].clean_alias, al_len);
234 return TRUE;
235 }
236 }
237 return FALSE;
238}
239
240
242{
244 int libraw_err = LIBRAW_SUCCESS;
245 if(!img->exif_inited) (void)dt_exif_read(img, filename);
246
247 libraw_data_t *raw = libraw_init(0);
249
250#if defined(_WIN32) && (defined(UNICODE) || defined(_UNICODE))
251 wchar_t *wfilename = g_utf8_to_utf16(filename, -1, NULL, NULL, NULL);
252 libraw_err = libraw_open_wfile(raw, wfilename);
253 dt_free(wfilename);
254#else
255 libraw_err = libraw_open_file(raw, filename);
256#endif
257 if(libraw_err != LIBRAW_SUCCESS) goto error;
258
259 libraw_err = libraw_unpack(raw);
260 if(libraw_err != LIBRAW_SUCCESS) goto error;
261
262 // Bad method to detect if camera is fully supported by LibRaw,
263 // but seems to be the best available. LibRaw crx decoder can actually
264 // decode the raw data, but internal metadata like wb_coeffs, crops etc.
265 // are not populated into libraw structure, or image is not of CFA type.
266 if(raw->rawdata.color.cam_mul[0] == 0.0f || isnan(raw->rawdata.color.cam_mul[0]) || IS_NULL_PTR(raw->rawdata.raw_image))
267 {
268 fprintf(stderr, "[libraw_open] detected unsupported image `%s'\n", img->filename);
269 goto error;
270 }
271
272 // Copy white level (all linear_max[] equal single SpecularWhiteLevel for CR3, we can skip min or mean)
273 img->raw_white_point = raw->rawdata.color.linear_max[0] ? raw->rawdata.color.linear_max[0] :raw->rawdata.color.maximum;
274
275 // Copy black level
276 img->raw_black_level = raw->rawdata.color.black;
277 for(size_t c = 0; c < 4; ++c)
278 img->raw_black_level_separate[c] = raw->rawdata.color.black + raw->rawdata.color.cblack[c];
279
280 // AsShot WB coeffs
281 for(size_t c = 0; c < 4; ++c)
282 img->wb_coeffs[c] = raw->rawdata.color.cam_mul[c];
283
284 // Grab the adobe coeff
285 for(int k = 0; k < 4; k++)
286 for(int i = 0; i < 3; i++)
287 img->adobe_XYZ_to_CAM[k][i] = raw->rawdata.color.cam_xyz[k][i];
288
289 // Raw dimensions. This is the full sensor range.
290 img->width = raw->rawdata.sizes.raw_width;
291 img->height = raw->rawdata.sizes.raw_height;
292
293 // Apply crop parameters
294 libraw_raw_inset_crop_t *ric = &raw->rawdata.sizes.raw_inset_crops[0];
295 img->crop_x = ric->cleft;
296 img->crop_y = ric->ctop;
297 img->crop_width = raw->rawdata.sizes.raw_width - ric->cwidth - ric->cleft;
298 img->crop_height = raw->rawdata.sizes.raw_height - ric->cheight - ric->ctop;
299
300 // In general we should run through entire post-processing to get corrected filters.
301 // This incurs a significant performance penalty.
302 libraw_err = libraw_dcraw_process(raw);
303 if(libraw_err != LIBRAW_SUCCESS) goto error;
304 img->dsc.filters = raw->idata.filters;
305
306 /* Deliberately NOT calling dt_exif_img_check_additional_tags() here, unlike the rawspeed
307 * path. It costs a second full Exiv2 open + parse on EVERY decode -- darkroom entry, each
308 * mipmap regeneration, each export -- and can find nothing on a libraw-decoded file
309 * today: this path serves Canon CR3, while every tag that function reads is a DNG tag
310 * (DefaultUserCrop, opcode lists) or a Sony/Fuji/Olympus maker note. The day a Canon
311 * correction decoder lands, add the call back WITH that decoder. */
312
313 // For CR3, we only have Bayer data and a single channel
314 img->dsc.channels = 1;
315 img->dsc.datatype = TYPE_UINT16;
316 img->dsc.bpp = sizeof(uint16_t);
317 img->dsc.cst = IOP_CS_RAW;
318
320 {
321 img->flags |= DT_IMAGE_4BAYER;
322 }
323 else
324 {
325 img->flags &= ~DT_IMAGE_4BAYER;
326 }
327
328 if(img->dsc.filters)
329 {
330 img->flags &= ~DT_IMAGE_LDR;
331 img->flags &= ~DT_IMAGE_HDR;
332 img->flags |= DT_IMAGE_RAW;
333 }
334 else
335 {
336 // ldr dng. it exists :(
337 img->flags &= ~DT_IMAGE_RAW;
338 img->flags &= ~DT_IMAGE_HDR;
339 img->flags |= DT_IMAGE_LDR;
340 }
341
342 img->loader = LOADER_LIBRAW;
343
344 if(IS_NULL_PTR(mbuf))
345 {
346 err = DT_IMAGEIO_OK;
347 goto error;
348 }
349
350 // Allocate and copy image from libraw buffer to dt
351 void *buf = dt_mipmap_cache_alloc(mbuf, img);
352 if(IS_NULL_PTR(buf))
353 {
354 fprintf(stderr, "[libraw_open] could not alloc full buffer for image `%s'\n", img->filename);
356 goto error;
357 }
358 // Use faster memcpy if buffer sizes are equal
359 const size_t bufSize_mipmap = (size_t)img->width * img->height * sizeof(uint16_t);
360 const size_t bufSize_libraw = (size_t)raw->rawdata.sizes.raw_pitch * raw->rawdata.sizes.raw_height;
361 if(bufSize_mipmap == bufSize_libraw)
362 {
363 memcpy(buf, raw->rawdata.raw_image, bufSize_mipmap);
364 }
365 else
366 {
367 dt_imageio_flip_buffers((char *)buf, (char *)raw->rawdata.raw_image, sizeof(uint16_t),
368 raw->rawdata.sizes.raw_width, raw->rawdata.sizes.raw_height,
369 raw->rawdata.sizes.raw_width, raw->rawdata.sizes.raw_height,
370 raw->rawdata.sizes.raw_pitch, ORIENTATION_NONE);
371 }
372
373 err = DT_IMAGEIO_OK;
374
375error:
376 if(libraw_err != LIBRAW_SUCCESS)
377 fprintf(stderr, "[libraw_open] `%s': %s\n", img->filename, libraw_strerror(libraw_err));
378 libraw_close(raw);
379 return err;
380}
381#endif
382
383// clang-format off
384// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
385// vim: shiftwidth=2 expandtab tabstop=2 cindent
386// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
387// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
@ IOP_CS_RAW
int dt_exif_read(dt_image_t *img, const char *path)
Definition exif.cc:1994
What a photograph says about itself: the EXIF, IPTC and XMP tags a camera and a cataloguer write,...
@ TYPE_UINT16
Definition format.h:57
@ ORIENTATION_NONE
Definition image.h:218
dt_imageio_retval_t
Definition image.h:91
@ DT_IMAGEIO_OK
Definition image.h:92
@ DT_IMAGEIO_CACHE_FULL
Definition image.h:95
@ DT_IMAGEIO_FILE_CORRUPTED
Definition image.h:94
@ DT_IMAGE_RAW
Definition image.h:124
@ DT_IMAGE_4BAYER
Definition image.h:140
@ DT_IMAGE_LDR
Definition image.h:122
@ LOADER_LIBRAW
Definition image.h:295
__DT_CLONE_TARGETS__ void dt_imageio_flip_buffers(char *out, const char *in, const size_t bpp, const int wd, const int ht, const int fwd, const int fht, const int stride, const dt_image_orientation_t orientation)
#define FILTERS_ARE_4BAYER(filters)
dt_imageio_retval_t dt_imageio_open_libraw(dt_image_t *img, const char *filename, dt_mipmap_buffer_t *buf)
gboolean dt_libraw_lookup_makermodel(const char *maker, const char *model, char *mk, int mk_len, char *md, int md_len, char *al, int al_len)
const char * maker
const char * model
float *const restrict const size_t k
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
void * dt_mipmap_cache_alloc(dt_mipmap_buffer_t *buf, const dt_image_t *img)
int32_t height
Definition image.h:397
uint16_t raw_black_level
Definition image.h:437
dt_image_loader_t loader
Definition image.h:417
uint32_t raw_white_point
Definition image.h:439
int32_t exif_inited
Definition image.h:365
int32_t crop_height
Definition image.h:398
int32_t flags
Definition image.h:401
int32_t width
Definition image.h:397
int32_t crop_y
Definition image.h:398
int32_t crop_x
Definition image.h:398
dt_iop_buffer_dsc_t dsc
Definition image.h:419
float adobe_XYZ_to_CAM[4][3]
Definition image.h:449
int32_t crop_width
Definition image.h:398
char filename[DT_MAX_FILENAME_LEN]
Definition image.h:386
uint16_t raw_black_level_separate[4]
Definition image.h:438
dt_aligned_pixel_t wb_coeffs
Definition image.h:446
uint32_t filters
Definition format.h:89
unsigned int channels
Definition format.h:83
dt_iop_buffer_type_t datatype
Definition format.h:85