/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 | } |