1 /*
2 This file is part of darktable,
3 Copyright (C) 2010-2020 darktable developers.
4
5 darktable is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 darktable is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #define __STDC_FORMAT_MACROS
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <assert.h>
26 #include <inttypes.h>
27 #include <memory>
28 #include <stdio.h>
29 #include <string.h>
30
31 #include <OpenEXR/ImfChannelList.h>
32 #include <OpenEXR/ImfFrameBuffer.h>
33 #include <OpenEXR/ImfInputFile.h>
34 #include <OpenEXR/ImfStandardAttributes.h>
35 #include <OpenEXR/ImfTestFile.h>
36 #include <OpenEXR/ImfThreading.h>
37 #include <OpenEXR/ImfTiledInputFile.h>
38
39 extern "C" {
40 #include "common/colorspaces.h"
41 #include "common/darktable.h"
42 #include "common/exif.h"
43 #include "common/imageio.h"
44 #include "common/imageio_exr.h"
45 #include "control/conf.h"
46 #include "develop/develop.h"
47 }
48 #include "common/imageio_exr.hh"
49
dt_imageio_open_exr(dt_image_t * img,const char * filename,dt_mipmap_buffer_t * mbuf)50 dt_imageio_retval_t dt_imageio_open_exr(dt_image_t *img, const char *filename, dt_mipmap_buffer_t *mbuf)
51 {
52 bool isTiled = false;
53
54 Imf::setGlobalThreadCount(dt_get_num_threads());
55
56 std::unique_ptr<Imf::TiledInputFile> fileTiled;
57 std::unique_ptr<Imf::InputFile> file;
58
59 Imath::Box2i dw;
60 Imf::FrameBuffer frameBuffer;
61 uint32_t xstride, ystride;
62
63
64 /* verify openexr image */
65 if(!Imf::isOpenExrFile((const char *)filename, isTiled)) return DT_IMAGEIO_FILE_CORRUPTED;
66
67 /* open exr file */
68 try
69 {
70 if(isTiled)
71 {
72 std::unique_ptr<Imf::TiledInputFile> temp(new Imf::TiledInputFile(filename));
73 fileTiled = std::move(temp);
74 }
75 else
76 {
77 std::unique_ptr<Imf::InputFile> temp(new Imf::InputFile(filename));
78 file = std::move(temp);
79 }
80 }
81 catch(const std::exception &e)
82 {
83 return DT_IMAGEIO_FILE_CORRUPTED;
84 }
85
86 const Imf::Header &header = isTiled ? fileTiled->header() : file->header();
87
88 /* check that channels available is any of supported RGB(a) */
89 bool hasR = false, hasG = false, hasB = false;
90 for(Imf::ChannelList::ConstIterator i = header.channels().begin(); i != header.channels().end(); ++i)
91 {
92 std::string name(i.name());
93 if(name == "R") hasR = true;
94 if(name == "G") hasG = true;
95 if(name == "B") hasB = true;
96 }
97 if(!(hasR && hasG && hasB))
98 {
99 fprintf(stderr, "[exr_read] Warning, only files with RGB(A) channels are supported.\n");
100 return DT_IMAGEIO_FILE_CORRUPTED;
101 }
102
103 if(!img->exif_inited)
104 {
105 // read back exif data
106 // if another software is able to update these exif data, the former test
107 // should be removed to take the potential changes in account (not done
108 // by normal import image flow)
109 const Imf::BlobAttribute *exif = header.findTypedAttribute<Imf::BlobAttribute>("exif");
110 // we append a jpg-compatible exif00 string, so get rid of that again:
111 if(exif && exif->value().size > 6)
112 dt_exif_read_from_blob(img, ((uint8_t *)(exif->value().data.get())) + 6, exif->value().size - 6);
113 }
114
115 /* Get image width and height from displayWindow */
116 dw = header.displayWindow();
117 img->width = dw.max.x - dw.min.x + 1;
118 img->height = dw.max.y - dw.min.y + 1;
119
120 // Try to allocate image data
121 img->buf_dsc.channels = 4;
122 img->buf_dsc.datatype = TYPE_FLOAT;
123 float *buf = (float *)dt_mipmap_cache_alloc(mbuf, img);
124 if(!buf)
125 {
126 fprintf(stderr, "[exr_read] could not alloc full buffer for image `%s'\n", img->filename);
127 /// \todo open exr cleanup...
128 return DT_IMAGEIO_CACHE_FULL;
129 }
130
131 // FIXME: is this really needed?
132 memset(buf, 0, sizeof(float) * 4 * img->width * img->height);
133
134 /* setup framebuffer */
135 xstride = sizeof(float) * 4;
136 ystride = sizeof(float) * img->width * 4;
137 frameBuffer.insert("R", Imf::Slice(Imf::FLOAT, (char *)(buf + 0), xstride, ystride, 1, 1, 0.0));
138 frameBuffer.insert("G", Imf::Slice(Imf::FLOAT, (char *)(buf + 1), xstride, ystride, 1, 1, 0.0));
139 frameBuffer.insert("B", Imf::Slice(Imf::FLOAT, (char *)(buf + 2), xstride, ystride, 1, 1, 0.0));
140 frameBuffer.insert("A", Imf::Slice(Imf::FLOAT, (char *)(buf + 3), xstride, ystride, 1, 1, 0.0));
141
142 if(isTiled)
143 {
144 fileTiled->setFrameBuffer(frameBuffer);
145 fileTiled->readTiles(0, fileTiled->numXTiles() - 1, 0, fileTiled->numYTiles() - 1);
146 }
147 else
148 {
149 /* read pixels from dataWindow */
150 dw = header.dataWindow();
151 file->setFrameBuffer(frameBuffer);
152 file->readPixels(dw.min.y, dw.max.y);
153 }
154
155 /* try to get the chromaticities and whitepoint. this will add the default linear rec709 profile when nothing
156 * was embedded and look as if it was embedded in colorin. better than defaulting to something wrong there. */
157 Imf::Chromaticities chromaticities;
158 float whiteLuminance = 1.0;
159
160 if(Imf::hasChromaticities(header))
161 chromaticities = Imf::chromaticities(header);
162
163 if(Imf::hasWhiteLuminance(header))
164 whiteLuminance = Imf::whiteLuminance(header);
165
166 // printf("hasChromaticities: %d\n", Imf::hasChromaticities(header));
167 // printf("hasWhiteLuminance: %d\n", Imf::hasWhiteLuminance(header));
168 // std::cout << chromaticities.red << std::endl;
169 // std::cout << chromaticities.green << std::endl;
170 // std::cout << chromaticities.blue << std::endl;
171 // std::cout << chromaticities.white << std::endl;
172
173 Imath::M44f m = Imf::RGBtoXYZ(chromaticities, whiteLuminance);
174 float mat[3][3];
175
176 for(int i = 0; i < 3; i++)
177 for(int j = 0; j < 3; j++)
178 {
179 mat[i][j] = m[j][i];
180 }
181
182 mat3inv((float *)img->d65_color_matrix, (float *)mat);
183
184
185 /* cleanup and return... */
186 img->buf_dsc.filters = 0u;
187 img->flags &= ~DT_IMAGE_RAW;
188 img->flags &= ~DT_IMAGE_S_RAW;
189 img->flags &= ~DT_IMAGE_LDR;
190 img->flags |= DT_IMAGE_HDR;
191
192 return DT_IMAGEIO_OK;
193 }
194
195 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
196 // vim: shiftwidth=2 expandtab tabstop=2 cindent
197 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
198