libopenraw  0.3.7
ciffifd.cpp
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 
33  : IfdDir(0, container, _type)
34  , m_file(ciff)
35 {
36 }
37 
38 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 
49  : CiffIfd(ciff, container, OR_IFD_MAIN)
50 {
51 }
52 
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
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 
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
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 
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 
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 
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 
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 
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 
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 
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 }
CiffExifIfd(CRWFile &ciff, RawContainer &container)
Construct an Exif IFD for a CIFF file.
Definition: ciffifd.cpp:101
virtual bool load() override
Load the directory to memory.
Definition: ciffifd.cpp:354
Special IFD to synthesize entries out of a CIFF file.
Definition: ciffifd.hpp:40
CiffIfd(CRWFile &ciff, RawContainer &container, IfdDirType _type)
construct a CIFF Ifd.
Definition: ciffifd.cpp:32
IfdEntry::Ref entryForString(uint16_t id, const std::string &str) const
Definition: ciffifd.cpp:38
CiffMainIfd(CRWFile &ciff, RawContainer &container)
Construct a main IFD for a CIFF file.
Definition: ciffifd.cpp:48
virtual bool load() override
Load the directory to memory.
Definition: ciffifd.cpp:53
A record entry from a CIFF Heap.
Definition: recordentry.hpp:69
boost::variant< InRec, InHeap > data
virtual RawContainer * getContainer() const override
Get the container.
Definition: crwfile.cpp:172
An IFD directory.
Definition: ifddir.hpp:51
const RawContainer & container() const
The container for the IfdDir, const.
Definition: ifddir.hpp:108
RawContainer::EndianType endian() const
the Container endian
Definition: ifddir.hpp:137
std::shared_ptr< IfdEntry > Ref
IfdEntry reference (ie shared pointer)
Definition: ifdentry.hpp:202
Generic interface for the RAW file container.
Tag class to help create an empty Option.
Definition: option.hpp:41
An option type inspired by Rust.
Definition: option.hpp:47
std::shared_ptr< Heap > HeapRef
Shared ptr to Heap.
Definition: heap.hpp:38
or_ifd_dir_type
Type of IfdDir.
Definition: consts.h:140
@ OR_IFD_MAIN
Main (like in TIFF)
Definition: consts.h:144
@ OR_IFD_EXIF
Exif metadata.
Definition: consts.h:146
std::map< uint16_t, RecordEntry > RecordEntries
Definition: recordentry.hpp:65
std::vector< uint16_t > CameraSettings
Camera settings are stored as array of 16-bits int.
Global namespace for libopenraw.
Definition: arwfile.cpp:29
Describe an IFD type.
Definition: ifdentry.hpp:55