Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
imageio_exr.cc
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2010, 2014 Henrik Andersson.
4 Copyright (C) 2010-2012, 2014 johannes hanika.
5 Copyright (C) 2012 Richard Wonka.
6 Copyright (C) 2012-2016 Tobias Ellinghaus.
7 Copyright (C) 2014, 2016 Roman Lebedev.
8 Copyright (C) 2019 Claude Heiland-Allen.
9 Copyright (C) 2020, 2025 Aurélien PIERRE.
10 Copyright (C) 2020 Hubert Kowalski.
11 Copyright (C) 2020-2021 Pascal Obry.
12 Copyright (C) 2020 Philippe Weyland.
13 Copyright (C) 2022 Martin Bařinka.
14 Copyright (C) 2022 Miloš Komarčević.
15 Copyright (C) 2023, 2025 Alynx Zhou.
16
17 darktable is free software: you can redistribute it and/or modify
18 it under the terms of the GNU General Public License as published by
19 the Free Software Foundation, either version 3 of the License, or
20 (at your option) any later version.
21
22 darktable is distributed in the hope that it will be useful,
23 but WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 GNU General Public License for more details.
26
27 You should have received a copy of the GNU General Public License
28 along with darktable. If not, see <http://www.gnu.org/licenses/>.
29*/
30
31#define __STDC_FORMAT_MACROS
32
33#ifdef HAVE_CONFIG_H
34#include "config.h"
35#endif
36
37#include <assert.h>
38#include <inttypes.h>
39#include <memory>
40#include <stdio.h>
41#include <string.h>
42
43#include <OpenEXR/ImfChannelList.h>
44#include <OpenEXR/ImfFrameBuffer.h>
45#include <OpenEXR/ImfInputFile.h>
46#include <OpenEXR/ImfStandardAttributes.h>
47#include <OpenEXR/ImfTestFile.h>
48#include <OpenEXR/ImfThreading.h>
49#include <OpenEXR/ImfTiledInputFile.h>
50
51#include "glib.h"
52
53#include "common/colorspaces.h"
54#include "common/darktable.h"
55#include "common/exif.h"
56#include "common/imageio.h"
57#include "common/imageio_exr.h"
58#include "control/conf.h"
59#include "develop/develop.h"
60
61#include "common/imageio_exr.hh"
62
64{
65 bool isTiled = false;
66
67 Imf::setGlobalThreadCount(darktable.num_openmp_threads);
68
69 std::unique_ptr<Imf::TiledInputFile> fileTiled;
70 std::unique_ptr<Imf::InputFile> file;
71
72 Imath::Box2i dw;
73 Imf::FrameBuffer frameBuffer;
74 uint32_t xstride, ystride;
75
76
77 /* verify openexr image */
78 if(!Imf::isOpenExrFile((const char *)filename, isTiled)) return DT_IMAGEIO_FILE_CORRUPTED;
79
80 /* open exr file */
81 try
82 {
83 if(isTiled)
84 {
85 std::unique_ptr<Imf::TiledInputFile> temp(new Imf::TiledInputFile(filename));
86 fileTiled = std::move(temp);
87 }
88 else
89 {
90 std::unique_ptr<Imf::InputFile> temp(new Imf::InputFile(filename));
91 file = std::move(temp);
92 }
93 }
94 catch(const std::exception &e)
95 {
97 }
98
99 const Imf::Header &header = isTiled ? fileTiled->header() : file->header();
100
101 /* check that channels available is any of supported RGB(a) */
102 bool hasR = false, hasG = false, hasB = false;
103 for(Imf::ChannelList::ConstIterator i = header.channels().begin(); i != header.channels().end(); ++i)
104 {
105 std::string name(i.name());
106 if(name == "R") hasR = true;
107 if(name == "G") hasG = true;
108 if(name == "B") hasB = true;
109 }
110 if(!(hasR && hasG && hasB))
111 {
112 fprintf(stderr, "[exr_read] Warning, only files with RGB(A) channels are supported.\n");
114 }
115
116 if(!img->exif_inited)
117 {
118 // read back exif data
119 // if another software is able to update these exif data, the former test
120 // should be removed to take the potential changes in account (not done
121 // by normal import image flow)
122 const Imf::BlobAttribute *exif = header.findTypedAttribute<Imf::BlobAttribute>("exif");
123 // we append a jpg-compatible exif00 string, so get rid of that again:
124 if(exif && exif->value().size > 6)
125 dt_exif_read_from_blob(img, ((uint8_t *)(exif->value().data.get())) + 6, exif->value().size - 6);
126 }
127
128 /* Get image width and height from displayWindow */
129 dw = header.displayWindow();
130 img->width = dw.max.x - dw.min.x + 1;
131 img->height = dw.max.y - dw.min.y + 1;
132
133 // Try to allocate image data
134 img->dsc.channels = 4;
135 img->dsc.datatype = TYPE_FLOAT;
136 img->dsc.bpp = 4 * sizeof(float);
137 img->dsc.cst = IOP_CS_RGB;
138 img->dsc.filters = 0u;
139 img->flags &= ~DT_IMAGE_RAW;
140 img->flags &= ~DT_IMAGE_S_RAW;
141 img->flags &= ~DT_IMAGE_LDR;
142 img->flags |= DT_IMAGE_HDR;
143 img->loader = LOADER_EXR;
144
145 if(IS_NULL_PTR(mbuf)) return DT_IMAGEIO_OK;
146
147 float *buf = (float *)dt_mipmap_cache_alloc(mbuf, img);
148 if(!buf)
149 {
150 fprintf(stderr, "[exr_read] could not alloc full buffer for image `%s'\n", img->filename);
153 }
154
155 // FIXME: is this really needed?
156 memset(buf, 0, sizeof(float) * 4 * img->width * img->height);
157
158 /* setup framebuffer */
159 xstride = sizeof(float) * 4;
160 ystride = sizeof(float) * img->width * 4;
161 frameBuffer.insert("R", Imf::Slice(Imf::FLOAT, (char *)(buf + 0), xstride, ystride, 1, 1, 0.0));
162 frameBuffer.insert("G", Imf::Slice(Imf::FLOAT, (char *)(buf + 1), xstride, ystride, 1, 1, 0.0));
163 frameBuffer.insert("B", Imf::Slice(Imf::FLOAT, (char *)(buf + 2), xstride, ystride, 1, 1, 0.0));
164 frameBuffer.insert("A", Imf::Slice(Imf::FLOAT, (char *)(buf + 3), xstride, ystride, 1, 1, 0.0));
165
166 if(isTiled)
167 {
168 fileTiled->setFrameBuffer(frameBuffer);
169 fileTiled->readTiles(0, fileTiled->numXTiles() - 1, 0, fileTiled->numYTiles() - 1);
170 }
171 else
172 {
173 /* read pixels from dataWindow */
174 dw = header.dataWindow();
175 file->setFrameBuffer(frameBuffer);
176 file->readPixels(dw.min.y, dw.max.y);
177 }
178
179 /* try to get the chromaticities and whitepoint. this will add the default linear rec709 profile when nothing
180 * was embedded and look as if it was embedded in colorin. better than defaulting to something wrong there. */
181 Imf::Chromaticities chromaticities;
182 float whiteLuminance = 1.0;
183
184 if(Imf::hasChromaticities(header))
185 {
186 chromaticities = Imf::chromaticities(header);
187
188 /* adapt chromaticities to D65 expected by colorin */
189 cmsCIExyY red_xy = { chromaticities.red[0], chromaticities.red[1], 1.0 };
190 cmsCIEXYZ srcRed;
191 cmsxyY2XYZ(&srcRed, &red_xy);
192
193 cmsCIExyY green_xy = { chromaticities.green[0], chromaticities.green[1], 1.0 };
194 cmsCIEXYZ srcGreen;
195 cmsxyY2XYZ(&srcGreen, &green_xy);
196
197 cmsCIExyY blue_xy = { chromaticities.blue[0], chromaticities.blue[1], 1.0 };
198 cmsCIEXYZ srcBlue;
199 cmsxyY2XYZ(&srcBlue, &blue_xy);
200
201 const cmsCIExyY srcWhite_xy = { chromaticities.white[0], chromaticities.white[1], 1.0 };
202 cmsCIEXYZ srcWhite;
203 cmsxyY2XYZ(&srcWhite, &srcWhite_xy);
204
205 /* use Imf::Chromaticities definition */
206 const cmsCIExyY d65_xy = {0.3127f, 0.3290f, 1.0};
207 cmsCIEXYZ d65;
208 cmsxyY2XYZ(&d65, &d65_xy);
209
210 cmsCIEXYZ dstRed;
211 cmsAdaptToIlluminant(&dstRed, &srcWhite, &d65, &srcRed);
212
213 cmsCIEXYZ dstGreen;
214 cmsAdaptToIlluminant(&dstGreen, &srcWhite, &d65, &srcGreen);
215
216 cmsCIEXYZ dstBlue;
217 cmsAdaptToIlluminant(&dstBlue, &srcWhite, &d65, &srcBlue);
218
219 cmsXYZ2xyY(&red_xy, &dstRed);
220 chromaticities.red[0] = (float)red_xy.x;
221 chromaticities.red[1] = (float)red_xy.y;
222
223 cmsXYZ2xyY(&green_xy, &dstGreen);
224 chromaticities.green[0] = (float)green_xy.x;
225 chromaticities.green[1] = (float)green_xy.y;
226
227 cmsXYZ2xyY(&blue_xy, &dstBlue);
228 chromaticities.blue[0] = (float)blue_xy.x;
229 chromaticities.blue[1] = (float)blue_xy.y;
230
231 chromaticities.white[0] = 0.3127f;
232 chromaticities.white[1] = 0.3290f;
233 }
234
235 if(Imf::hasWhiteLuminance(header))
236 whiteLuminance = Imf::whiteLuminance(header);
237
238// printf("hasChromaticities: %d\n", Imf::hasChromaticities(header));
239// printf("hasWhiteLuminance: %d\n", Imf::hasWhiteLuminance(header));
240// std::cout << chromaticities.red << std::endl;
241// std::cout << chromaticities.green << std::endl;
242// std::cout << chromaticities.blue << std::endl;
243// std::cout << chromaticities.white << std::endl;
244
245 Imath::M44f m = Imf::XYZtoRGB(chromaticities, whiteLuminance);
246
247 for(int i = 0; i < 3; i++)
248 for(int j = 0; j < 3; j++)
249 {
250 img->d65_color_matrix[3 * i + j] = m[j][i];
251 }
252
253 return DT_IMAGEIO_OK;
254}
255
256// clang-format off
257// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
258// vim: shiftwidth=2 expandtab tabstop=2 cindent
259// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
260// clang-format on
#define m
Definition basecurve.c:278
@ IOP_CS_RGB
static const cmsCIEXYZ d65
char * name
darktable_t darktable
Definition darktable.c:181
#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
int dt_exif_read_from_blob(dt_image_t *img, uint8_t *blob, const int size)
Definition exif.cc:1682
@ TYPE_FLOAT
Definition format.h:46
dt_imageio_retval_t
Definition image.h:78
@ DT_IMAGEIO_OK
Definition image.h:79
@ DT_IMAGEIO_CACHE_FULL
Definition image.h:82
@ DT_IMAGEIO_FILE_CORRUPTED
Definition image.h:81
@ DT_IMAGE_HDR
Definition image.h:113
@ LOADER_EXR
Definition image.h:227
dt_imageio_retval_t dt_imageio_open_exr(dt_image_t *img, const char *filename, dt_mipmap_buffer_t *mbuf)
void * dt_mipmap_cache_alloc(dt_mipmap_buffer_t *buf, const dt_image_t *img)
Imf ::TypedAttribute< Imf ::Blob > BlobAttribute
int32_t num_openmp_threads
Definition darktable.h:758
int32_t height
Definition image.h:315
dt_image_loader_t loader
Definition image.h:335
int32_t exif_inited
Definition image.h:283
int32_t flags
Definition image.h:319
int32_t width
Definition image.h:315
float d65_color_matrix[9]
Definition image.h:339
dt_iop_buffer_dsc_t dsc
Definition image.h:337
char filename[DT_MAX_FILENAME_LEN]
Definition image.h:304
uint32_t filters
Definition format.h:60
unsigned int channels
Definition format.h:54
dt_iop_buffer_type_t datatype
Definition format.h:56