1 /* -*- Mode: C++; c-basic-offset:4; tab-width:4; indent-tabs-mode:nil; -*- */
2 /*
3 * libopenraw - ciffifd.hpp
4 *
5 * Copyright (C) 2020 Hubert Figuière
6 *
7 * This library is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public License
9 * as published by the Free Software Foundation, either version 3 of
10 * the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see
19 * <http://www.gnu.org/licenses/>.
20 */
21
22 #include "endianutils.hpp"
23 #include "ifd.hpp"
24 #include "ciffifd.hpp"
25 #include "ciffcontainer.hpp"
26 #include "crwfile.hpp"
27
28 namespace OpenRaw {
29 namespace Internals {
30 namespace CIFF {
31
CiffIfd(CRWFile & ciff,RawContainer & container,IfdDirType _type)32 CiffIfd::CiffIfd(CRWFile& ciff, RawContainer& container, IfdDirType _type)
33 : IfdDir(0, container, _type)
34 , m_file(ciff)
35 {
36 }
37
entryForString(uint16_t id,const std::string & str) const38 IfdEntry::Ref CiffIfd::entryForString(uint16_t id, const std::string& str) const
39 {
40 // We include the terminating NUL.
41 // std::string::c_str() returns it.
42 auto entry = std::make_shared<IfdEntry>(id, IFD::EXIF_FORMAT_ASCII,
43 str.size() + 1, 0, *this, true);
44 entry->setData(reinterpret_cast<const uint8_t*>(str.c_str()), str.size() + 1);
45 return entry;
46 }
47
CiffMainIfd(CRWFile & ciff,RawContainer & container)48 CiffMainIfd::CiffMainIfd(CRWFile& ciff, RawContainer& container)
49 : CiffIfd(ciff, container, OR_IFD_MAIN)
50 {
51 }
52
load()53 bool CiffMainIfd::load()
54 {
55 auto img_spec = static_cast<CIFFContainer*>(m_file.getContainer())->getImageSpec();
56 if (img_spec) {
57 auto w = img_spec->imageWidth;
58 auto h = img_spec->imageHeight;
59 auto bpc = img_spec->componentBitDepth;
60
61 // The data field in ifdentry is in container endian
62 if (endian() == RawContainer::ENDIAN_BIG) {
63 w = htobe16(w);
64 h = htobe16(h);
65 bpc = htobe16(bpc);
66 } else {
67 w = htole16(w);
68 h = htole16(h);
69 bpc = htole16(bpc);
70 }
71 auto entry = std::make_shared<IfdEntry>(EXIF_TAG_IMAGE_WIDTH, EXIF_FORMAT_SHORT,
72 1, w, *this, true);
73 m_entries[EXIF_TAG_IMAGE_WIDTH] = entry;
74 entry = std::make_shared<IfdEntry>(EXIF_TAG_IMAGE_LENGTH, EXIF_FORMAT_SHORT,
75 1, h, *this, true);
76 m_entries[EXIF_TAG_IMAGE_LENGTH] = entry;
77 entry = std::make_shared<IfdEntry>(EXIF_TAG_BITS_PER_SAMPLE, EXIF_FORMAT_SHORT,
78 1, bpc, *this, true);
79 m_entries[EXIF_TAG_BITS_PER_SAMPLE] = entry;
80 }
81
82 auto val = m_file.getOrientation();
83 if (val) {
84 auto entry = std::make_shared<IfdEntry>(EXIF_TAG_ORIENTATION, EXIF_FORMAT_SHORT,
85 1, val.value(), *this, true);
86 m_entries[EXIF_TAG_ORIENTATION] = entry;
87 }
88 auto val_str = m_file.getMakeOrModel(EXIF_TAG_MAKE);
89 if (val_str) {
90 auto entry = entryForString(EXIF_TAG_MAKE, val_str.value());
91 m_entries[EXIF_TAG_MAKE] = entry;
92 }
93 val_str = m_file.getMakeOrModel(EXIF_TAG_MODEL);
94 if (val_str) {
95 auto entry = entryForString(EXIF_TAG_MODEL, val_str.value());
96 m_entries[EXIF_TAG_MODEL] = entry;
97 }
98 return true;
99 }
100
CiffExifIfd(CRWFile & ciff,RawContainer & container)101 CiffExifIfd::CiffExifIfd(CRWFile& ciff, RawContainer& container)
102 : CiffIfd(ciff, container, OR_IFD_EXIF)
103 {
104 }
105
106 namespace {
107
108 typedef std::vector<IfdEntry::Ref> (*Converter)(const RecordEntry& e, Heap& heap, CiffIfd& ifd, uint16_t exifTag);
109
110 struct Ciff2Exif {
111 uint16_t exifTag;
112 or_ifd_dir_type dest;
113 Option<Converter> converter;
114 };
115
116 // TAG_FOCALLENGTH to Exif
translateFocalLength(const RecordEntry & e,Heap &,CiffIfd & ifd,uint16_t exifTag)117 std::vector<IfdEntry::Ref> translateFocalLength(const RecordEntry& e, Heap&, CiffIfd& ifd, uint16_t exifTag)
118 {
119 LOGASSERT(e.inRecord());
120 uint32_t fl;
121 uint32_t fu = 0;
122 auto data = boost::get<RecordEntry::InRec>(e.data);
123 if (ifd.container().endian() == RawContainer::ENDIAN_LITTLE) {
124 fl = IfdTypeTrait<uint16_t>::EL(data.c_array() + 2, sizeof(uint16_t));
125 } else {
126 fl = IfdTypeTrait<uint16_t>::BE(data.c_array() + 2, sizeof(uint16_t));
127 }
128
129 CIFFContainer* ciffc = dynamic_cast<CIFFContainer*>(&ifd.container());
130 auto csettings = ciffc->getCameraSettings();
131 if (csettings.size() >= 26) {
132 fu = csettings[25];
133 }
134
135 uint32_t r[] = { fl, fu };
136 auto ifdentry = std::make_shared<IfdEntry>(exifTag, EXIF_FORMAT_RATIONAL, 1, 0, ifd, true);
137 ifdentry->setData(reinterpret_cast<uint8_t*>(&r), 8);
138 return { ifdentry };
139 }
140
translateDate(const RecordEntry & e,Heap & heap,CiffIfd & ifd,uint16_t)141 std::vector<IfdEntry::Ref> translateDate(const RecordEntry& e, Heap& heap, CiffIfd& ifd, uint16_t)
142 {
143 struct tm d;
144 uint32_t data[3];
145 e.fetchData(&heap, &data, 12);
146 time_t t = data[0];
147 char date[] = "0000:00:00 00:00:00";
148 auto d2 = gmtime_r(&t, &d);
149 if (d2) {
150 strftime(date, 20, "%Y:%m:%d %H:%M:%S", d2);
151 }
152 return {
153 ifd.entryForString(EXIF_TAG_DATE_TIME_ORIGINAL, date),
154 ifd.entryForString(EXIF_TAG_DATE_TIME_DIGITIZED, date),
155 };
156 }
157
translateSerial(const RecordEntry & e,Heap &,CiffIfd & ifd,uint16_t exifTag)158 std::vector<IfdEntry::Ref> translateSerial(const RecordEntry& e, Heap& , CiffIfd& ifd, uint16_t exifTag)
159 {
160 uint32_t serial_v;
161 LOGASSERT(e.inRecord());
162 auto data = boost::get<RecordEntry::InRec>(e.data);
163 if (ifd.container().endian() == RawContainer::ENDIAN_LITTLE) {
164 serial_v = IfdTypeTrait<uint32_t>::EL(data.c_array(), sizeof(uint32_t));
165 } else {
166 serial_v = IfdTypeTrait<uint32_t>::BE(data.c_array() + 2, sizeof(uint32_t));
167 }
168 char serial[10];
169 snprintf(serial, 10, "%X", serial_v);
170 return { ifd.entryForString(exifTag, serial) };
171 }
172
translateString(const RecordEntry & e,Heap & heap,CiffIfd & ifd,uint16_t exifTag)173 std::vector<IfdEntry::Ref> translateString(const RecordEntry& e, Heap& heap, CiffIfd& ifd, uint16_t exifTag)
174 {
175 std::string val_str = e.getString(heap);
176 return { ifd.entryForString(exifTag, val_str) };
177 }
178
translateMakeModel(const RecordEntry & e,Heap & heap,CiffIfd & ifd,uint16_t exifTag)179 std::vector<IfdEntry::Ref> translateMakeModel(const RecordEntry& e, Heap& heap, CiffIfd& ifd, uint16_t exifTag)
180 {
181 return { ifd.entryForString(exifTag, e.getString(heap)) };
182 }
183
translateCameraSettings(const RecordEntry & e,Heap & heap,CiffIfd & ifd,uint16_t)184 std::vector<IfdEntry::Ref> translateCameraSettings(const RecordEntry& e, Heap& heap, CiffIfd& ifd, uint16_t /*exifTag*/)
185 {
186 std::vector<IfdEntry::Ref> entries;
187 auto count = e.count();
188 CIFF::CameraSettings settings;
189 auto file = ifd.container().file();
190 file->seek(heap.offset() + e.offset(), SEEK_SET);
191 size_t countRead = ifd.container().readUInt16Array(file, settings, count);
192 if (count != countRead) {
193 LOGERR("Not enough data for camera settings\n");
194 } else {
195 for (uint32_t i = 0; i < count; i++) {
196 switch (i) {
197 case 1: // Macro Mode
198 if (settings[i] == 1) {
199 auto ifdentry = std::make_shared<IfdEntry>(
200 EXIF_TAG_SUBJECT_DISTANCE_RANGE, EXIF_FORMAT_SHORT, 1,
201 1, ifd, true);
202 entries.push_back(ifdentry);
203 }
204 break;
205 case 4: { // Flash mode
206 uint16_t flash = 0;
207 switch (settings[i]) {
208 case 0:
209 // off
210 break;
211 case 1:
212 // Auto
213 flash = 0x19;
214 break;
215 case 2:
216 // on
217 flash = 0x01;
218 break;
219 case 3:
220 case 5:
221 // red-eye
222 flash = 0x41;
223 break;
224 }
225 auto ifdentry = std::make_shared<IfdEntry>(
226 EXIF_TAG_FLASH, EXIF_FORMAT_SHORT, 1, flash, ifd, true);
227 entries.push_back(ifdentry);
228 break;
229 }
230 case 17: { // Metering mode
231 uint16_t metering = 0;
232 switch (settings[i]) {
233 case 0: // Default
234 break;
235 case 1: // Spot
236 metering = 3;
237 break;
238 case 2: // Average
239 metering = 1;
240 break;
241 case 3: // Evaluative
242 metering = 5;
243 break;
244 case 4: // Partial
245 metering = 6;
246 break;
247 case 5: // Center-weigthed average
248 metering = 2;
249 break;
250 default:
251 break;
252 }
253 auto ifdentry = std::make_shared<IfdEntry>(
254 EXIF_TAG_METERING_MODE, EXIF_FORMAT_SHORT, 1, metering, ifd, true);
255 entries.push_back(ifdentry);
256 break;
257 }
258 case 20: { // Exposure mode
259 uint16_t exposure = 0;
260 switch (settings[i]) {
261 case 0: // Easy
262 break;
263 case 1: // Program AE
264 exposure = 2;
265 break;
266 case 2: // Shutter prio
267 exposure = 4;
268 break;
269 case 3: // Aperture prio
270 exposure = 3;
271 break;
272 case 4: // Manual
273 exposure = 1;
274 break;
275 case 5: // DoF
276 exposure = 5;
277 break;
278 case 6: // M-Dep
279 case 7: // Bulb
280 case 8: // Flexible
281 default:
282 break;
283 }
284 auto ifdentry = std::make_shared<IfdEntry>(
285 EXIF_TAG_METERING_MODE, EXIF_FORMAT_SHORT, 1, exposure, ifd, true);
286 entries.push_back(ifdentry);
287 break;
288 }
289 default:
290 break;
291 }
292 }
293 }
294
295 return entries;
296 }
297
298 static const std::multimap<uint16_t, Ciff2Exif> ciff_exif_map = {
299 { TAG_FOCALLENGTH, { EXIF_TAG_FOCAL_LENGTH, OR_IFD_EXIF, &translateFocalLength } },
300 { TAG_FILEDESCRIPTION, { EXIF_TAG_IMAGE_DESCRIPTION, OR_IFD_MAIN, OptionNone() } },
301 { TAG_ORIGINALFILENAME, { EXIF_TAG_DOCUMENT_NAME, OR_IFD_MAIN, OptionNone() } },
302 { TAG_TARGETDISTANCESETTING, { EXIF_TAG_SUBJECT_DISTANCE, OR_IFD_EXIF, OptionNone() } },
303 { TAG_RAWMAKEMODEL, { EXIF_TAG_MAKE, OR_IFD_MAIN, &translateMakeModel } },
304 { TAG_RAWMAKEMODEL, { EXIF_TAG_MODEL, OR_IFD_MAIN, &translateMakeModel } },
305 { TAG_OWNERNAME, { EXIF_TAG_CAMERA_OWNER_NAME, OR_IFD_EXIF, &translateString } },
306 { TAG_SERIALNUMBER, { EXIF_TAG_BODY_SERIAL_NUMBER, OR_IFD_EXIF, &translateSerial } },
307 { TAG_CAPTUREDTIME, { 0, OR_IFD_EXIF, &translateDate } },
308 { TAG_CAMERASETTINGS, { 0, OR_IFD_EXIF, &translateCameraSettings } }
309 };
310
translateRecordEntry(const RecordEntry & e,Heap & heap,CiffIfd & ifd)311 std::vector<IfdEntry::Ref> translateRecordEntry(const RecordEntry& e, Heap& heap, CiffIfd& ifd)
312 {
313 std::vector<IfdEntry::Ref> vec;
314 if (e.isHeap()) {
315 const CIFFContainer* ciffc = dynamic_cast<const CIFFContainer*>(&ifd.container());
316 LOGASSERT(ciffc);
317 Heap h = e.heap(heap, ciffc);
318 for (const auto& rec : h.records()) {
319 auto r = translateRecordEntry(rec.second, h, ifd);
320 vec.insert(vec.begin(), r.begin(), r.end());
321 }
322 return vec;
323 }
324 auto iter = ciff_exif_map.find(TAGCODE(e.typeCode));
325 if (iter != ciff_exif_map.end()) {
326 do {
327 if (iter->first != (TAGCODE(e.typeCode))) {
328 break;
329 }
330 // printf("tag 0x%x mapped to 0x%x in %d\n", iter->first, iter->second.exifTag,
331 // iter->second.dest);
332 if (iter->second.dest == ifd.type()) {
333 if (iter->second.converter) {
334 auto values =
335 iter->second.converter.value_ref()(e, heap, ifd, iter->second.exifTag);
336 vec.insert(vec.end(), values.begin(), values.end());
337 } else {
338 vec.push_back(
339 std::make_shared<IfdEntry>(
340 iter->second.exifTag, e.exifType(), e.count(), 0, ifd));
341 }
342 }
343 iter++;
344 } while(iter != ciff_exif_map.end());
345 } else {
346 // printf("No mapping for 0x%x\n", e.typeCode & TAGCODE_MASK);
347 }
348
349 return vec;
350 }
351
352 }
353
load()354 bool CiffExifIfd::load()
355 {
356 auto container = static_cast<CIFFContainer*>(m_file.getContainer());
357 HeapRef props = container->getImageProps();
358 if (props) {
359 const RecordEntries& propsRecs = props->records();
360 for (const auto& rec : propsRecs) {
361 auto ifdentries = translateRecordEntry(rec.second, *props, *this);
362 if (!ifdentries.empty()) {
363 for (auto entry2 : ifdentries) {
364 m_entries[entry2->id()] = entry2;
365 }
366 }
367 }
368
369 HeapRef exifProps = container->getExifInfo();
370 if (exifProps) {
371 for (const auto& rec : exifProps->records()) {
372 auto ifdentries = translateRecordEntry(rec.second, *exifProps, *this);
373 if (!ifdentries.empty()) {
374 for (auto entry2 : ifdentries) {
375 m_entries[entry2->id()] = entry2;
376 }
377 }
378 }
379 }
380 }
381 return true;
382 }
383
384 }
385 }
386 }
387