Coverage Report

Created: 2026-01-25 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/LibRaw/src/metadata/exif_gps.cpp
Line
Count
Source
1
/* -*- C++ -*-
2
 * Copyright 2019-2025 LibRaw LLC (info@libraw.org)
3
 *
4
 LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
5
 dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
6
 LibRaw do not use RESTRICTED code from dcraw.c
7
8
 LibRaw is free software; you can redistribute it and/or modify
9
 it under the terms of the one of two licenses as you choose:
10
11
1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
12
   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
13
14
2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
15
   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
16
17
 */
18
19
#include "../../internal/dcraw_defs.h"
20
#include "../../internal/libraw_cameraids.h"
21
22
void LibRaw::parse_exif_interop(INT64 base)
23
0
{
24
0
  unsigned entries, tag, type, len;
25
0
  INT64 save;
26
0
  char value[4] = { 0,0,0,0 };
27
0
  entries = get2();
28
0
  INT64 fsize = ifp->size();
29
0
  while (entries--)
30
0
  {
31
0
    tiff_get(base, &tag, &type, &len, &save);
32
33
0
    INT64 savepos = ftell(ifp);
34
0
    if (len > 8 && savepos + len > fsize * 2)
35
0
    {
36
0
      fseek(ifp, save, SEEK_SET); // Recover tiff-read position!!
37
0
      continue;
38
0
    }
39
0
        if (callbacks.exif_cb)
40
0
        {
41
0
            callbacks.exif_cb(callbacks.exifparser_data, tag | 0x40000, type, len, order, ifp, base);
42
0
            fseek(ifp, savepos, SEEK_SET);
43
0
        }
44
45
0
    switch (tag)
46
0
    {
47
0
    case 0x0001: // InteropIndex
48
0
      fread(value, 1, MIN(4, len), ifp);
49
0
      if (strncmp(value, "R98", 3) == 0 &&
50
        // Canon bug, when [Canon].ColorSpace = AdobeRGB,
51
        // but [ExifIFD].ColorSpace = Uncalibrated and
52
        // [InteropIFD].InteropIndex = "R98"
53
0
        imgdata.color.ExifColorSpace == LIBRAW_COLORSPACE_Unknown)
54
0
        imgdata.color.ExifColorSpace = LIBRAW_COLORSPACE_sRGB;
55
0
      else if (strncmp(value, "R03", 3) == 0)
56
0
        imgdata.color.ExifColorSpace = LIBRAW_COLORSPACE_AdobeRGB;
57
0
      break;
58
0
    }
59
0
    fseek(ifp, save, SEEK_SET);
60
0
  }
61
0
}
62
63
void LibRaw::parse_exif(INT64 base)
64
0
{
65
0
  unsigned entries, tag, type, len, c;
66
0
  double expo, ape;
67
0
  INT64 save;
68
69
0
  unsigned kodak = !strncmp(make, "EASTMAN", 7) && tiff_nifds < 3;
70
71
0
  if (!libraw_internal_data.unpacker_data.exif_subdir_offset)
72
0
  {
73
0
    libraw_internal_data.unpacker_data.exif_offset = base;
74
0
    libraw_internal_data.unpacker_data.exif_subdir_offset = ftell(ifp);
75
0
  }
76
77
0
  entries = get2();
78
0
  if (!strncmp(make, "Hasselblad", 10) && (tiff_nifds > 3) && (entries > 512))
79
0
    return;
80
0
  INT64 fsize = ifp->size();
81
0
  while (entries--)
82
0
  {
83
0
    tiff_get(base, &tag, &type, &len, &save);
84
85
0
    INT64 savepos = ftell(ifp);
86
0
    if (len > 8 && savepos + INT64(len) > fsize * 2LL)
87
0
    {
88
0
      fseek(ifp, save, SEEK_SET); // Recover tiff-read position!!
89
0
      continue;
90
0
    }
91
0
    if (callbacks.exif_cb)
92
0
    {
93
0
      callbacks.exif_cb(callbacks.exifparser_data, tag, type, len, order, ifp,
94
0
                        base);
95
0
      fseek(ifp, savepos, SEEK_SET);
96
0
    }
97
98
0
    switch (tag)
99
0
    {
100
0
  case 0xA005: // Interoperability IFD
101
0
    fseek(ifp, get4() + base, SEEK_SET);
102
0
    parse_exif_interop(base);
103
0
    break;
104
0
  case 0xA001: // ExifIFD.ColorSpace
105
0
    c = get2();
106
0
    if (c == 1 && imgdata.color.ExifColorSpace == LIBRAW_COLORSPACE_Unknown)
107
0
      imgdata.color.ExifColorSpace = LIBRAW_COLORSPACE_sRGB;
108
0
    else if (c == 2)
109
0
      imgdata.color.ExifColorSpace = LIBRAW_COLORSPACE_AdobeRGB;
110
0
    break;
111
0
    case 0x9400:
112
0
      imCommon.exifAmbientTemperature = getrealf(type);
113
0
      if ((imCommon.CameraTemperature > -273.15f) &&
114
0
          ((OlyID == OlyID_TG_5) ||
115
0
           (OlyID == OlyID_TG_6))
116
0
      )
117
0
        imCommon.CameraTemperature += imCommon.exifAmbientTemperature;
118
0
      break;
119
0
    case 0x9401:
120
0
      imCommon.exifHumidity = getrealf(type);
121
0
      break;
122
0
    case 0x9402:
123
0
      imCommon.exifPressure = getrealf(type);
124
0
      break;
125
0
    case 0x9403:
126
0
      imCommon.exifWaterDepth = getrealf(type);
127
0
      break;
128
0
    case 0x9404:
129
0
      imCommon.exifAcceleration = getrealf(type);
130
0
      break;
131
0
    case 0x9405:
132
0
      imCommon.exifCameraElevationAngle = getrealf(type);
133
0
      break;
134
135
0
    case 0xa405: // FocalLengthIn35mmFormat
136
0
      imgdata.lens.FocalLengthIn35mmFormat = get2();
137
0
      break;
138
0
    case 0xa431: // BodySerialNumber
139
0
      stmread(imgdata.shootinginfo.BodySerial, len, ifp);
140
0
      break;
141
0
    case 0xa432: // LensInfo, 42034dec, Lens Specification per EXIF standard
142
0
      imgdata.lens.MinFocal = getrealf(type);
143
0
      imgdata.lens.MaxFocal = getrealf(type);
144
0
      imgdata.lens.MaxAp4MinFocal = getrealf(type);
145
0
      imgdata.lens.MaxAp4MaxFocal = getrealf(type);
146
0
      break;
147
0
    case 0xa435: // LensSerialNumber
148
0
      stmread(imgdata.lens.LensSerial, len, ifp);
149
0
      if (!strncmp(imgdata.lens.LensSerial, "----", 4))
150
0
        imgdata.lens.LensSerial[0] = '\0';
151
0
      break;
152
0
    case 0xa420: /* 42016, ImageUniqueID */
153
0
      stmread(imgdata.color.ImageUniqueID, len, ifp);
154
0
      break;
155
0
    case 0xc65d: /* 50781, RawDataUniqueID */
156
0
      imgdata.color.RawDataUniqueID[16] = 0;
157
0
      fread(imgdata.color.RawDataUniqueID, 1, 16, ifp);
158
0
      break;
159
0
    case 0xc630: // DNG LensInfo, Lens Specification per EXIF standard
160
0
      imgdata.lens.dng.MinFocal = getrealf(type);
161
0
      imgdata.lens.dng.MaxFocal = getrealf(type);
162
0
      imgdata.lens.dng.MaxAp4MinFocal = getrealf(type);
163
0
      imgdata.lens.dng.MaxAp4MaxFocal = getrealf(type);
164
0
      break;
165
0
    case 0xc68b: /* 50827, OriginalRawFileName */
166
0
      stmread(imgdata.color.OriginalRawFileName, len, ifp);
167
0
      break;
168
0
    case 0xa433: // LensMake
169
0
      stmread(imgdata.lens.LensMake, len, ifp);
170
0
      break;
171
0
    case 0xa434: // LensModel
172
0
      stmread(imgdata.lens.Lens, len, ifp);
173
0
      if (!strncmp(imgdata.lens.Lens, "----", 4))
174
0
        imgdata.lens.Lens[0] = '\0';
175
0
      break;
176
0
    case 0x9205:
177
0
      imgdata.lens.EXIF_MaxAp = libraw_powf64l(2.0f, getrealf(type) / 2.0f);
178
0
      break;
179
0
    case 0x829a: // 33434
180
0
      shutter = getrealf(type);
181
0
      if (tiff_nifds > 0 && tiff_nifds <= LIBRAW_IFD_MAXCOUNT)
182
0
          tiff_ifd[tiff_nifds - 1].t_shutter = shutter;
183
0
      break;
184
0
    case 0x829d: // 33437, FNumber
185
0
      aperture = getrealf(type);
186
0
      break;
187
0
    case 0x8827: // 34855
188
0
      iso_speed = get2();
189
0
      break;
190
0
    case 0x8831: // 34865
191
0
      if (iso_speed == 0xffff && !strncasecmp(make, "FUJI", 4))
192
0
        iso_speed = getrealf(type);
193
0
      break;
194
0
    case 0x8832: // 34866
195
0
      if (iso_speed == 0xffff &&
196
0
          (!strncasecmp(make, "SONY", 4) || !strncasecmp(make, "CANON", 5)))
197
0
        iso_speed = getrealf(type);
198
0
      break;
199
0
    case 0x9003: // 36867
200
0
    case 0x9004: // 36868
201
0
      get_timestamp(0);
202
0
      break;
203
0
    case 0x9201: // 37377
204
0
       if ((expo = -getreal(type)) < 128 && shutter == 0.)
205
0
       {
206
0
            shutter = libraw_powf64l(2.0f, float(expo));
207
0
            if (tiff_nifds > 0 && tiff_nifds <= LIBRAW_IFD_MAXCOUNT)
208
0
              tiff_ifd[tiff_nifds - 1].t_shutter = shutter;
209
0
       }
210
0
      break;
211
0
    case 0x9202: // 37378 ApertureValue
212
0
      if ((fabs(ape = getreal(type)) < 256.0) && (!aperture))
213
0
        aperture = libraw_powf64l(2.0f, float(ape / 2.0));
214
0
      break;
215
0
    case 0x9209: // 37385
216
0
      flash_used = getrealf(type);
217
0
      break;
218
0
    case 0x920a: // 37386
219
0
      focal_len = getrealf(type);
220
0
      break;
221
0
    case 0x927c: // 37500
222
0
#ifndef USE_6BY9RPI
223
0
      if (((make[0] == '\0') && !strncmp(model, "ov5647", 6)) ||
224
0
          (!strncmp(make, "RaspberryPi", 11) &&
225
0
           (!strncmp(model, "RP_OV5647", 9) ||
226
0
            !strncmp(model, "RP_imx219", 9))))
227
#else
228
      if (((make[0] == '\0') && !strncmp(model, "ov5647", 6)) ||
229
          (!strncmp(make, "RaspberryPi", 11) &&
230
              (!strncmp(model, "RP_", 3) || !strncmp(model,"imx477",6))))
231
#endif
232
0
      {
233
0
        char mn_text[512];
234
0
        char *pos;
235
0
        char ccms[512];
236
0
        ushort l;
237
0
        float num;
238
239
0
        fgets(mn_text, MIN(len, 511), ifp);
240
0
        mn_text[511] = 0;
241
242
0
        pos = strstr(mn_text, "ev=");
243
0
        if (pos)
244
0
          imCommon.ExposureCalibrationShift = float(atof(pos + 3));
245
246
0
        pos = strstr(mn_text, "gain_r=");
247
0
        if (pos)
248
0
          cam_mul[0] = float(atof(pos + 7));
249
0
        pos = strstr(mn_text, "gain_b=");
250
0
        if (pos)
251
0
          cam_mul[2] = float(atof(pos + 7));
252
0
        if ((cam_mul[0] > 0.001f) && (cam_mul[2] > 0.001f))
253
0
          cam_mul[1] = cam_mul[3] = 1.0f;
254
0
        else
255
0
          cam_mul[0] = cam_mul[2] = 0.0f;
256
257
0
        pos = strstr(mn_text, "ccm=");
258
0
        if (pos)
259
0
        {
260
0
          pos += 4;
261
0
          char *pos2 = strstr(pos, " ");
262
0
          if (pos2)
263
0
          {
264
0
            l = LIM(ushort(pos2 - pos), 0, 511);
265
0
            memcpy(ccms, pos, l);
266
0
            ccms[l] = '\0';
267
#ifdef LIBRAW_WIN32_CALLS
268
            // Win32 strtok is already thread-safe
269
            pos = strtok(ccms, ",");
270
#else
271
0
            char *last = 0;
272
0
            pos = strtok_r(ccms, ",", &last);
273
0
#endif
274
0
            if (pos)
275
0
            {
276
0
              for (l = 0; l < 3; l++) // skip last row
277
0
              {
278
0
                num = 0.0;
279
0
                for (c = 0; c < 3; c++)
280
0
                {
281
0
                  cmatrix[l][c] = (float)atoi(pos);
282
0
                  num += cmatrix[c][l];
283
#ifdef LIBRAW_WIN32_CALLS
284
                  pos = strtok(NULL, ",");
285
#else
286
0
                  pos = strtok_r(NULL, ",", &last);
287
0
#endif
288
0
                  if (!pos)
289
0
                    goto end; // broken
290
0
                }
291
0
                if (num > 0.01)
292
0
                    FORC3 cmatrix[l][c] = cmatrix[l][c] / num;
293
0
              }
294
0
            }
295
0
          }
296
0
        }
297
0
      end:;
298
0
      }
299
0
      else if (!strncmp(make, "SONY", 4) &&
300
0
               (!strncmp(model, "DSC-V3", 6) || !strncmp(model, "DSC-F828", 8)))
301
0
      {
302
0
        parseSonySRF(len);
303
0
        break;
304
0
      }
305
0
      else if ((len == 1) && !strncmp(make, "NIKON", 5))
306
0
      {
307
0
        c = get4();
308
0
        if (c)
309
0
          fseek(ifp, c, SEEK_SET);
310
0
        is_NikonTransfer = 1;
311
0
      }
312
0
      parse_makernote(base, 0);
313
0
      break;
314
0
    case 0xa002: // 40962
315
0
      if (kodak)
316
0
        raw_width = get4();
317
0
      break;
318
0
    case 0xa003: // 40963
319
0
      if (kodak)
320
0
        raw_height = get4();
321
0
      break;
322
0
    case 0xa302: // 41730
323
0
      if (get4() == 0x20002)
324
0
        for (exif_cfa = c = 0; c < 8; c += 2)
325
0
          exif_cfa |= fgetc(ifp) * 0x01010101U << c;
326
0
    }
327
0
    fseek(ifp, save, SEEK_SET);
328
0
  }
329
0
}
330
331
void LibRaw::parse_gps_libraw(INT64 base)
332
0
{
333
0
  unsigned entries, tag, type, len, c;
334
0
  INT64 save;
335
336
0
  entries = get2();
337
0
  if (entries > 40)
338
0
    return;
339
0
  if (entries > 0)
340
0
    imgdata.other.parsed_gps.gpsparsed = 1;
341
0
  INT64 fsize = ifp->size();
342
0
  while (entries--)
343
0
  {
344
0
    tiff_get(base, &tag, &type, &len, &save);
345
0
    if (len > 1024)
346
0
    {
347
0
      fseek(ifp, save, SEEK_SET); // Recover tiff-read position!!
348
0
      continue;                   // no GPS tags are 1k or larger
349
0
    }
350
0
    INT64 savepos = ftell(ifp);
351
0
    if (len > 8 && savepos + len > fsize * 2)
352
0
    {
353
0
        fseek(ifp, save, SEEK_SET); // Recover tiff-read position!!
354
0
        continue;
355
0
    }
356
357
0
    if (callbacks.exif_cb)
358
0
    {
359
0
        callbacks.exif_cb(callbacks.exifparser_data, tag | 0x50000, type, len, order, ifp, base);
360
0
        fseek(ifp, savepos, SEEK_SET);
361
0
    }
362
363
0
    switch (tag)
364
0
    {
365
0
    case 0x0001:
366
0
      imgdata.other.parsed_gps.latref = getc(ifp);
367
0
      break;
368
0
    case 0x0003:
369
0
      imgdata.other.parsed_gps.longref = getc(ifp);
370
0
      break;
371
0
    case 0x0005:
372
0
      imgdata.other.parsed_gps.altref = getc(ifp);
373
0
      break;
374
0
    case 0x0002:
375
0
      if (len == 3)
376
0
        FORC(3) imgdata.other.parsed_gps.latitude[c] = getrealf(type);
377
0
      break;
378
0
    case 0x0004:
379
0
      if (len == 3)
380
0
        FORC(3) imgdata.other.parsed_gps.longitude[c] = getrealf(type);
381
0
      break;
382
0
    case 0x0007:
383
0
      if (len == 3)
384
0
        FORC(3) imgdata.other.parsed_gps.gpstimestamp[c] = getrealf(type);
385
0
      break;
386
0
    case 0x0006:
387
0
      imgdata.other.parsed_gps.altitude = getrealf(type);
388
0
      break;
389
0
    case 0x0009:
390
0
      imgdata.other.parsed_gps.gpsstatus = getc(ifp);
391
0
      break;
392
0
    }
393
0
    fseek(ifp, save, SEEK_SET);
394
0
  }
395
0
}
396
397
void LibRaw::parse_gps(INT64 base)
398
0
{
399
0
  unsigned entries, tag, type, len, c;
400
0
  INT64 save;
401
402
0
  entries = get2();
403
0
  if (entries > 40)
404
0
    return;
405
0
  while (entries--)
406
0
  {
407
0
    tiff_get(base, &tag, &type, &len, &save);
408
0
    if (len > 1024)
409
0
    {
410
0
      fseek(ifp, save, SEEK_SET); // Recover tiff-read position!!
411
0
      continue;                   // no GPS tags are 1k or larger
412
0
    }
413
0
    switch (tag)
414
0
    {
415
0
    case 0x0001:
416
0
    case 0x0003:
417
0
    case 0x0005:
418
0
      gpsdata[29 + tag / 2] = getc(ifp);
419
0
      break;
420
0
    case 0x0002:
421
0
    case 0x0004:
422
0
    case 0x0007:
423
0
      FORC(6) gpsdata[tag / 3 * 6 + c] = get4();
424
0
      break;
425
0
    case 0x0006:
426
0
      FORC(2) gpsdata[18 + c] = get4();
427
0
      break;
428
0
    case 0x0012: // 18
429
0
    case 0x001d: // 29
430
0
      fgets((char *)(gpsdata + 14 + tag / 3), MIN(len, 12), ifp);
431
0
    }
432
0
    fseek(ifp, save, SEEK_SET);
433
0
  }
434
0
}