rjhead 0.2.88
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.rdoc +18 -0
- data/Rakefile +57 -0
- data/VERSION +1 -0
- data/ext/changes.txt +351 -0
- data/ext/exif.c +1629 -0
- data/ext/extconf.rb +0 -0
- data/ext/gpsinfo.c +217 -0
- data/ext/iptc.c +205 -0
- data/ext/jhead.1 +409 -0
- data/ext/jhead.c +1697 -0
- data/ext/jhead.h +251 -0
- data/ext/jhead.spec +149 -0
- data/ext/jpgfile.c +738 -0
- data/ext/make.bat +1 -0
- data/ext/makefile +23 -0
- data/ext/makefile-win32 +27 -0
- data/ext/makernote.c +184 -0
- data/ext/myglob.c +305 -0
- data/ext/paths.c +140 -0
- data/ext/readme.txt +60 -0
- data/ext/usage.html +469 -0
- data/lib/rjhead.rb +0 -0
- data/rjhead.gemspec +73 -0
- data/setup.rake +1 -0
- data/test/rjhead_test.rb +7 -0
- data/test/test_helper.rb +10 -0
- metadata +94 -0
data/ext/exif.c
ADDED
@@ -0,0 +1,1629 @@
|
|
1
|
+
//--------------------------------------------------------------------------
|
2
|
+
// Program to pull the information out of various types of EXIF digital
|
3
|
+
// camera files and show it in a reasonably consistent way
|
4
|
+
//
|
5
|
+
// This module parses the very complicated exif structures.
|
6
|
+
//
|
7
|
+
// Matthias Wandel
|
8
|
+
//--------------------------------------------------------------------------
|
9
|
+
#include "jhead.h"
|
10
|
+
|
11
|
+
#include <math.h>
|
12
|
+
|
13
|
+
static unsigned char * DirWithThumbnailPtrs;
|
14
|
+
static double FocalplaneXRes;
|
15
|
+
static double FocalplaneUnits;
|
16
|
+
static int ExifImageWidth;
|
17
|
+
static int MotorolaOrder = 0;
|
18
|
+
|
19
|
+
// for fixing the rotation.
|
20
|
+
static void * OrientationPtr[2];
|
21
|
+
static int OrientationNumFormat[2];
|
22
|
+
int NumOrientations = 0;
|
23
|
+
|
24
|
+
typedef struct {
|
25
|
+
unsigned short Tag;
|
26
|
+
char * Desc;
|
27
|
+
}TagTable_t;
|
28
|
+
|
29
|
+
|
30
|
+
//--------------------------------------------------------------------------
|
31
|
+
// Table of Jpeg encoding process names
|
32
|
+
static const TagTable_t ProcessTable[] = {
|
33
|
+
{ M_SOF0, "Baseline"},
|
34
|
+
{ M_SOF1, "Extended sequential"},
|
35
|
+
{ M_SOF2, "Progressive"},
|
36
|
+
{ M_SOF3, "Lossless"},
|
37
|
+
{ M_SOF5, "Differential sequential"},
|
38
|
+
{ M_SOF6, "Differential progressive"},
|
39
|
+
{ M_SOF7, "Differential lossless"},
|
40
|
+
{ M_SOF9, "Extended sequential, arithmetic coding"},
|
41
|
+
{ M_SOF10, "Progressive, arithmetic coding"},
|
42
|
+
{ M_SOF11, "Lossless, arithmetic coding"},
|
43
|
+
{ M_SOF13, "Differential sequential, arithmetic coding"},
|
44
|
+
{ M_SOF14, "Differential progressive, arithmetic coding"},
|
45
|
+
{ M_SOF15, "Differential lossless, arithmetic coding"},
|
46
|
+
};
|
47
|
+
|
48
|
+
#define PROCESS_TABLE_SIZE (sizeof(ProcessTable) / sizeof(TagTable_t))
|
49
|
+
|
50
|
+
// 1 - "The 0th row is at the visual top of the image, and the 0th column is the visual left-hand side."
|
51
|
+
// 2 - "The 0th row is at the visual top of the image, and the 0th column is the visual right-hand side."
|
52
|
+
// 3 - "The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side."
|
53
|
+
// 4 - "The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side."
|
54
|
+
|
55
|
+
// 5 - "The 0th row is the visual left-hand side of of the image, and the 0th column is the visual top."
|
56
|
+
// 6 - "The 0th row is the visual right-hand side of of the image, and the 0th column is the visual top."
|
57
|
+
// 7 - "The 0th row is the visual right-hand side of of the image, and the 0th column is the visual bottom."
|
58
|
+
// 8 - "The 0th row is the visual left-hand side of of the image, and the 0th column is the visual bottom."
|
59
|
+
|
60
|
+
// Note: The descriptions here are the same as the name of the command line
|
61
|
+
// option to pass to jpegtran to right the image
|
62
|
+
|
63
|
+
static const char * OrientTab[9] = {
|
64
|
+
"Undefined",
|
65
|
+
"Normal", // 1
|
66
|
+
"flip horizontal", // left right reversed mirror
|
67
|
+
"rotate 180", // 3
|
68
|
+
"flip vertical", // upside down mirror
|
69
|
+
"transpose", // Flipped about top-left <--> bottom-right axis.
|
70
|
+
"rotate 90", // rotate 90 cw to right it.
|
71
|
+
"transverse", // flipped about top-right <--> bottom-left axis
|
72
|
+
"rotate 270", // rotate 270 to right it.
|
73
|
+
};
|
74
|
+
|
75
|
+
const int BytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8};
|
76
|
+
|
77
|
+
//--------------------------------------------------------------------------
|
78
|
+
// Describes tag values
|
79
|
+
|
80
|
+
#define TAG_INTEROP_INDEX 0x0001
|
81
|
+
#define TAG_INTEROP_VERSION 0x0002
|
82
|
+
#define TAG_IMAGE_WIDTH 0x0100
|
83
|
+
#define TAG_IMAGE_LENGTH 0x0101
|
84
|
+
#define TAG_BITS_PER_SAMPLE 0x0102
|
85
|
+
#define TAG_COMPRESSION 0x0103
|
86
|
+
#define TAG_PHOTOMETRIC_INTERP 0x0106
|
87
|
+
#define TAG_FILL_ORDER 0x010A
|
88
|
+
#define TAG_DOCUMENT_NAME 0x010D
|
89
|
+
#define TAG_IMAGE_DESCRIPTION 0x010E
|
90
|
+
#define TAG_MAKE 0x010F
|
91
|
+
#define TAG_MODEL 0x0110
|
92
|
+
#define TAG_SRIP_OFFSET 0x0111
|
93
|
+
#define TAG_ORIENTATION 0x0112
|
94
|
+
#define TAG_SAMPLES_PER_PIXEL 0x0115
|
95
|
+
#define TAG_ROWS_PER_STRIP 0x0116
|
96
|
+
#define TAG_STRIP_BYTE_COUNTS 0x0117
|
97
|
+
#define TAG_X_RESOLUTION 0x011A
|
98
|
+
#define TAG_Y_RESOLUTION 0x011B
|
99
|
+
#define TAG_PLANAR_CONFIGURATION 0x011C
|
100
|
+
#define TAG_RESOLUTION_UNIT 0x0128
|
101
|
+
#define TAG_TRANSFER_FUNCTION 0x012D
|
102
|
+
#define TAG_SOFTWARE 0x0131
|
103
|
+
#define TAG_DATETIME 0x0132
|
104
|
+
#define TAG_ARTIST 0x013B
|
105
|
+
#define TAG_WHITE_POINT 0x013E
|
106
|
+
#define TAG_PRIMARY_CHROMATICITIES 0x013F
|
107
|
+
#define TAG_TRANSFER_RANGE 0x0156
|
108
|
+
#define TAG_JPEG_PROC 0x0200
|
109
|
+
#define TAG_THUMBNAIL_OFFSET 0x0201
|
110
|
+
#define TAG_THUMBNAIL_LENGTH 0x0202
|
111
|
+
#define TAG_Y_CB_CR_COEFFICIENTS 0x0211
|
112
|
+
#define TAG_Y_CB_CR_SUB_SAMPLING 0x0212
|
113
|
+
#define TAG_Y_CB_CR_POSITIONING 0x0213
|
114
|
+
#define TAG_REFERENCE_BLACK_WHITE 0x0214
|
115
|
+
#define TAG_RELATED_IMAGE_WIDTH 0x1001
|
116
|
+
#define TAG_RELATED_IMAGE_LENGTH 0x1002
|
117
|
+
#define TAG_CFA_REPEAT_PATTERN_DIM 0x828D
|
118
|
+
#define TAG_CFA_PATTERN1 0x828E
|
119
|
+
#define TAG_BATTERY_LEVEL 0x828F
|
120
|
+
#define TAG_COPYRIGHT 0x8298
|
121
|
+
#define TAG_EXPOSURETIME 0x829A
|
122
|
+
#define TAG_FNUMBER 0x829D
|
123
|
+
#define TAG_IPTC_NAA 0x83BB
|
124
|
+
#define TAG_EXIF_OFFSET 0x8769
|
125
|
+
#define TAG_INTER_COLOR_PROFILE 0x8773
|
126
|
+
#define TAG_EXPOSURE_PROGRAM 0x8822
|
127
|
+
#define TAG_SPECTRAL_SENSITIVITY 0x8824
|
128
|
+
#define TAG_GPSINFO 0x8825
|
129
|
+
#define TAG_ISO_EQUIVALENT 0x8827
|
130
|
+
#define TAG_OECF 0x8828
|
131
|
+
#define TAG_EXIF_VERSION 0x9000
|
132
|
+
#define TAG_DATETIME_ORIGINAL 0x9003
|
133
|
+
#define TAG_DATETIME_DIGITIZED 0x9004
|
134
|
+
#define TAG_COMPONENTS_CONFIG 0x9101
|
135
|
+
#define TAG_CPRS_BITS_PER_PIXEL 0x9102
|
136
|
+
#define TAG_SHUTTERSPEED 0x9201
|
137
|
+
#define TAG_APERTURE 0x9202
|
138
|
+
#define TAG_BRIGHTNESS_VALUE 0x9203
|
139
|
+
#define TAG_EXPOSURE_BIAS 0x9204
|
140
|
+
#define TAG_MAXAPERTURE 0x9205
|
141
|
+
#define TAG_SUBJECT_DISTANCE 0x9206
|
142
|
+
#define TAG_METERING_MODE 0x9207
|
143
|
+
#define TAG_LIGHT_SOURCE 0x9208
|
144
|
+
#define TAG_FLASH 0x9209
|
145
|
+
#define TAG_FOCALLENGTH 0x920A
|
146
|
+
#define TAG_SUBJECTAREA 0x9214
|
147
|
+
#define TAG_MAKER_NOTE 0x927C
|
148
|
+
#define TAG_USERCOMMENT 0x9286
|
149
|
+
#define TAG_SUBSEC_TIME 0x9290
|
150
|
+
#define TAG_SUBSEC_TIME_ORIG 0x9291
|
151
|
+
#define TAG_SUBSEC_TIME_DIG 0x9292
|
152
|
+
|
153
|
+
#define TAG_WINXP_TITLE 0x9c9b // Windows XP - not part of exif standard.
|
154
|
+
#define TAG_WINXP_COMMENT 0x9c9c // Windows XP - not part of exif standard.
|
155
|
+
#define TAG_WINXP_AUTHOR 0x9c9d // Windows XP - not part of exif standard.
|
156
|
+
#define TAG_WINXP_KEYWORDS 0x9c9e // Windows XP - not part of exif standard.
|
157
|
+
#define TAG_WINXP_SUBJECT 0x9c9f // Windows XP - not part of exif standard.
|
158
|
+
|
159
|
+
#define TAG_FLASH_PIX_VERSION 0xA000
|
160
|
+
#define TAG_COLOR_SPACE 0xA001
|
161
|
+
#define TAG_PIXEL_X_DIMENSION 0xA002
|
162
|
+
#define TAG_PIXEL_Y_DIMENSION 0xA003
|
163
|
+
#define TAG_RELATED_AUDIO_FILE 0xA004
|
164
|
+
#define TAG_INTEROP_OFFSET 0xA005
|
165
|
+
#define TAG_FLASH_ENERGY 0xA20B
|
166
|
+
#define TAG_SPATIAL_FREQ_RESP 0xA20C
|
167
|
+
#define TAG_FOCAL_PLANE_XRES 0xA20E
|
168
|
+
#define TAG_FOCAL_PLANE_YRES 0xA20F
|
169
|
+
#define TAG_FOCAL_PLANE_UNITS 0xA210
|
170
|
+
#define TAG_SUBJECT_LOCATION 0xA214
|
171
|
+
#define TAG_EXPOSURE_INDEX 0xA215
|
172
|
+
#define TAG_SENSING_METHOD 0xA217
|
173
|
+
#define TAG_FILE_SOURCE 0xA300
|
174
|
+
#define TAG_SCENE_TYPE 0xA301
|
175
|
+
#define TAG_CFA_PATTERN 0xA302
|
176
|
+
#define TAG_CUSTOM_RENDERED 0xA401
|
177
|
+
#define TAG_EXPOSURE_MODE 0xA402
|
178
|
+
#define TAG_WHITEBALANCE 0xA403
|
179
|
+
#define TAG_DIGITALZOOMRATIO 0xA404
|
180
|
+
#define TAG_FOCALLENGTH_35MM 0xA405
|
181
|
+
#define TAG_SCENE_CAPTURE_TYPE 0xA406
|
182
|
+
#define TAG_GAIN_CONTROL 0xA407
|
183
|
+
#define TAG_CONTRAST 0xA408
|
184
|
+
#define TAG_SATURATION 0xA409
|
185
|
+
#define TAG_SHARPNESS 0xA40A
|
186
|
+
#define TAG_DISTANCE_RANGE 0xA40C
|
187
|
+
#define TAG_IMAGE_UNIQUE_ID 0xA420
|
188
|
+
|
189
|
+
static const TagTable_t TagTable[] = {
|
190
|
+
{ TAG_INTEROP_INDEX, "InteropIndex"},
|
191
|
+
{ TAG_INTEROP_VERSION, "InteropVersion"},
|
192
|
+
{ TAG_IMAGE_WIDTH, "ImageWidth"},
|
193
|
+
{ TAG_IMAGE_LENGTH, "ImageLength"},
|
194
|
+
{ TAG_BITS_PER_SAMPLE, "BitsPerSample"},
|
195
|
+
{ TAG_COMPRESSION, "Compression"},
|
196
|
+
{ TAG_PHOTOMETRIC_INTERP, "PhotometricInterpretation"},
|
197
|
+
{ TAG_FILL_ORDER, "FillOrder"},
|
198
|
+
{ TAG_DOCUMENT_NAME, "DocumentName"},
|
199
|
+
{ TAG_IMAGE_DESCRIPTION, "ImageDescription"},
|
200
|
+
{ TAG_MAKE, "Make"},
|
201
|
+
{ TAG_MODEL, "Model"},
|
202
|
+
{ TAG_SRIP_OFFSET, "StripOffsets"},
|
203
|
+
{ TAG_ORIENTATION, "Orientation"},
|
204
|
+
{ TAG_SAMPLES_PER_PIXEL, "SamplesPerPixel"},
|
205
|
+
{ TAG_ROWS_PER_STRIP, "RowsPerStrip"},
|
206
|
+
{ TAG_STRIP_BYTE_COUNTS, "StripByteCounts"},
|
207
|
+
{ TAG_X_RESOLUTION, "XResolution"},
|
208
|
+
{ TAG_Y_RESOLUTION, "YResolution"},
|
209
|
+
{ TAG_PLANAR_CONFIGURATION, "PlanarConfiguration"},
|
210
|
+
{ TAG_RESOLUTION_UNIT, "ResolutionUnit"},
|
211
|
+
{ TAG_TRANSFER_FUNCTION, "TransferFunction"},
|
212
|
+
{ TAG_SOFTWARE, "Software"},
|
213
|
+
{ TAG_DATETIME, "DateTime"},
|
214
|
+
{ TAG_ARTIST, "Artist"},
|
215
|
+
{ TAG_WHITE_POINT, "WhitePoint"},
|
216
|
+
{ TAG_PRIMARY_CHROMATICITIES, "PrimaryChromaticities"},
|
217
|
+
{ TAG_TRANSFER_RANGE, "TransferRange"},
|
218
|
+
{ TAG_JPEG_PROC, "JPEGProc"},
|
219
|
+
{ TAG_THUMBNAIL_OFFSET, "ThumbnailOffset"},
|
220
|
+
{ TAG_THUMBNAIL_LENGTH, "ThumbnailLength"},
|
221
|
+
{ TAG_Y_CB_CR_COEFFICIENTS, "YCbCrCoefficients"},
|
222
|
+
{ TAG_Y_CB_CR_SUB_SAMPLING, "YCbCrSubSampling"},
|
223
|
+
{ TAG_Y_CB_CR_POSITIONING, "YCbCrPositioning"},
|
224
|
+
{ TAG_REFERENCE_BLACK_WHITE, "ReferenceBlackWhite"},
|
225
|
+
{ TAG_RELATED_IMAGE_WIDTH, "RelatedImageWidth"},
|
226
|
+
{ TAG_RELATED_IMAGE_LENGTH, "RelatedImageLength"},
|
227
|
+
{ TAG_CFA_REPEAT_PATTERN_DIM, "CFARepeatPatternDim"},
|
228
|
+
{ TAG_CFA_PATTERN1, "CFAPattern"},
|
229
|
+
{ TAG_BATTERY_LEVEL, "BatteryLevel"},
|
230
|
+
{ TAG_COPYRIGHT, "Copyright"},
|
231
|
+
{ TAG_EXPOSURETIME, "ExposureTime"},
|
232
|
+
{ TAG_FNUMBER, "FNumber"},
|
233
|
+
{ TAG_IPTC_NAA, "IPTC/NAA"},
|
234
|
+
{ TAG_EXIF_OFFSET, "ExifOffset"},
|
235
|
+
{ TAG_INTER_COLOR_PROFILE, "InterColorProfile"},
|
236
|
+
{ TAG_EXPOSURE_PROGRAM, "ExposureProgram"},
|
237
|
+
{ TAG_SPECTRAL_SENSITIVITY, "SpectralSensitivity"},
|
238
|
+
{ TAG_GPSINFO, "GPS Dir offset"},
|
239
|
+
{ TAG_ISO_EQUIVALENT, "ISOSpeedRatings"},
|
240
|
+
{ TAG_OECF, "OECF"},
|
241
|
+
{ TAG_EXIF_VERSION, "ExifVersion"},
|
242
|
+
{ TAG_DATETIME_ORIGINAL, "DateTimeOriginal"},
|
243
|
+
{ TAG_DATETIME_DIGITIZED, "DateTimeDigitized"},
|
244
|
+
{ TAG_COMPONENTS_CONFIG, "ComponentsConfiguration"},
|
245
|
+
{ TAG_CPRS_BITS_PER_PIXEL, "CompressedBitsPerPixel"},
|
246
|
+
{ TAG_SHUTTERSPEED, "ShutterSpeedValue"},
|
247
|
+
{ TAG_APERTURE, "ApertureValue"},
|
248
|
+
{ TAG_BRIGHTNESS_VALUE, "BrightnessValue"},
|
249
|
+
{ TAG_EXPOSURE_BIAS, "ExposureBiasValue"},
|
250
|
+
{ TAG_MAXAPERTURE, "MaxApertureValue"},
|
251
|
+
{ TAG_SUBJECT_DISTANCE, "SubjectDistance"},
|
252
|
+
{ TAG_METERING_MODE, "MeteringMode"},
|
253
|
+
{ TAG_LIGHT_SOURCE, "LightSource"},
|
254
|
+
{ TAG_FLASH, "Flash"},
|
255
|
+
{ TAG_FOCALLENGTH, "FocalLength"},
|
256
|
+
{ TAG_MAKER_NOTE, "MakerNote"},
|
257
|
+
{ TAG_USERCOMMENT, "UserComment"},
|
258
|
+
{ TAG_SUBSEC_TIME, "SubSecTime"},
|
259
|
+
{ TAG_SUBSEC_TIME_ORIG, "SubSecTimeOriginal"},
|
260
|
+
{ TAG_SUBSEC_TIME_DIG, "SubSecTimeDigitized"},
|
261
|
+
{ TAG_WINXP_TITLE, "Windows-XP Title"},
|
262
|
+
{ TAG_WINXP_COMMENT, "Windows-XP comment"},
|
263
|
+
{ TAG_WINXP_AUTHOR, "Windows-XP author"},
|
264
|
+
{ TAG_WINXP_KEYWORDS, "Windows-XP keywords"},
|
265
|
+
{ TAG_WINXP_SUBJECT, "Windows-XP subject"},
|
266
|
+
{ TAG_FLASH_PIX_VERSION, "FlashPixVersion"},
|
267
|
+
{ TAG_COLOR_SPACE, "ColorSpace"},
|
268
|
+
{ TAG_PIXEL_X_DIMENSION, "ExifImageWidth"},
|
269
|
+
{ TAG_PIXEL_Y_DIMENSION, "ExifImageLength"},
|
270
|
+
{ TAG_RELATED_AUDIO_FILE, "RelatedAudioFile"},
|
271
|
+
{ TAG_INTEROP_OFFSET, "InteroperabilityOffset"},
|
272
|
+
{ TAG_FLASH_ENERGY, "FlashEnergy"},
|
273
|
+
{ TAG_SPATIAL_FREQ_RESP, "SpatialFrequencyResponse"},
|
274
|
+
{ TAG_FOCAL_PLANE_XRES, "FocalPlaneXResolution"},
|
275
|
+
{ TAG_FOCAL_PLANE_YRES, "FocalPlaneYResolution"},
|
276
|
+
{ TAG_FOCAL_PLANE_UNITS, "FocalPlaneResolutionUnit"},
|
277
|
+
{ TAG_SUBJECT_LOCATION, "SubjectLocation"},
|
278
|
+
{ TAG_EXPOSURE_INDEX, "ExposureIndex"},
|
279
|
+
{ TAG_SENSING_METHOD, "SensingMethod"},
|
280
|
+
{ TAG_FILE_SOURCE, "FileSource"},
|
281
|
+
{ TAG_SCENE_TYPE, "SceneType"},
|
282
|
+
{ TAG_CFA_PATTERN, "CFA Pattern"},
|
283
|
+
{ TAG_CUSTOM_RENDERED, "CustomRendered"},
|
284
|
+
{ TAG_EXPOSURE_MODE, "ExposureMode"},
|
285
|
+
{ TAG_WHITEBALANCE, "WhiteBalance"},
|
286
|
+
{ TAG_DIGITALZOOMRATIO, "DigitalZoomRatio"},
|
287
|
+
{ TAG_FOCALLENGTH_35MM, "FocalLengthIn35mmFilm"},
|
288
|
+
{ TAG_SUBJECTAREA, "SubjectArea"},
|
289
|
+
{ TAG_SCENE_CAPTURE_TYPE, "SceneCaptureType"},
|
290
|
+
{ TAG_GAIN_CONTROL, "GainControl"},
|
291
|
+
{ TAG_CONTRAST, "Contrast"},
|
292
|
+
{ TAG_SATURATION, "Saturation"},
|
293
|
+
{ TAG_SHARPNESS, "Sharpness"},
|
294
|
+
{ TAG_DISTANCE_RANGE, "SubjectDistanceRange"},
|
295
|
+
{ TAG_IMAGE_UNIQUE_ID, "ImageUniqueId"},
|
296
|
+
} ;
|
297
|
+
|
298
|
+
#define TAG_TABLE_SIZE (sizeof(TagTable) / sizeof(TagTable_t))
|
299
|
+
|
300
|
+
|
301
|
+
//--------------------------------------------------------------------------
|
302
|
+
// Convert a 16 bit unsigned value to file's native byte order
|
303
|
+
//--------------------------------------------------------------------------
|
304
|
+
static void Put16u(void * Short, unsigned short PutValue)
|
305
|
+
{
|
306
|
+
if (MotorolaOrder){
|
307
|
+
((uchar *)Short)[0] = (uchar)(PutValue>>8);
|
308
|
+
((uchar *)Short)[1] = (uchar)PutValue;
|
309
|
+
}else{
|
310
|
+
((uchar *)Short)[0] = (uchar)PutValue;
|
311
|
+
((uchar *)Short)[1] = (uchar)(PutValue>>8);
|
312
|
+
}
|
313
|
+
}
|
314
|
+
|
315
|
+
//--------------------------------------------------------------------------
|
316
|
+
// Convert a 16 bit unsigned value from file's native byte order
|
317
|
+
//--------------------------------------------------------------------------
|
318
|
+
int Get16u(void * Short)
|
319
|
+
{
|
320
|
+
if (MotorolaOrder){
|
321
|
+
return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1];
|
322
|
+
}else{
|
323
|
+
return (((uchar *)Short)[1] << 8) | ((uchar *)Short)[0];
|
324
|
+
}
|
325
|
+
}
|
326
|
+
|
327
|
+
//--------------------------------------------------------------------------
|
328
|
+
// Convert a 32 bit signed value from file's native byte order
|
329
|
+
//--------------------------------------------------------------------------
|
330
|
+
int Get32s(void * Long)
|
331
|
+
{
|
332
|
+
if (MotorolaOrder){
|
333
|
+
return ((( char *)Long)[0] << 24) | (((uchar *)Long)[1] << 16)
|
334
|
+
| (((uchar *)Long)[2] << 8 ) | (((uchar *)Long)[3] << 0 );
|
335
|
+
}else{
|
336
|
+
return ((( char *)Long)[3] << 24) | (((uchar *)Long)[2] << 16)
|
337
|
+
| (((uchar *)Long)[1] << 8 ) | (((uchar *)Long)[0] << 0 );
|
338
|
+
}
|
339
|
+
}
|
340
|
+
|
341
|
+
//--------------------------------------------------------------------------
|
342
|
+
// Convert a 32 bit unsigned value to file's native byte order
|
343
|
+
//--------------------------------------------------------------------------
|
344
|
+
void Put32u(void * Value, unsigned PutValue)
|
345
|
+
{
|
346
|
+
if (MotorolaOrder){
|
347
|
+
((uchar *)Value)[0] = (uchar)(PutValue>>24);
|
348
|
+
((uchar *)Value)[1] = (uchar)(PutValue>>16);
|
349
|
+
((uchar *)Value)[2] = (uchar)(PutValue>>8);
|
350
|
+
((uchar *)Value)[3] = (uchar)PutValue;
|
351
|
+
}else{
|
352
|
+
((uchar *)Value)[0] = (uchar)PutValue;
|
353
|
+
((uchar *)Value)[1] = (uchar)(PutValue>>8);
|
354
|
+
((uchar *)Value)[2] = (uchar)(PutValue>>16);
|
355
|
+
((uchar *)Value)[3] = (uchar)(PutValue>>24);
|
356
|
+
}
|
357
|
+
}
|
358
|
+
|
359
|
+
//--------------------------------------------------------------------------
|
360
|
+
// Convert a 32 bit unsigned value from file's native byte order
|
361
|
+
//--------------------------------------------------------------------------
|
362
|
+
unsigned Get32u(void * Long)
|
363
|
+
{
|
364
|
+
return (unsigned)Get32s(Long) & 0xffffffff;
|
365
|
+
}
|
366
|
+
|
367
|
+
//--------------------------------------------------------------------------
|
368
|
+
// Display a number as one of its many formats
|
369
|
+
//--------------------------------------------------------------------------
|
370
|
+
void PrintFormatNumber(void * ValuePtr, int Format, int ByteCount)
|
371
|
+
{
|
372
|
+
int s,n;
|
373
|
+
|
374
|
+
for(n=0;n<16;n++){
|
375
|
+
switch(Format){
|
376
|
+
case FMT_SBYTE:
|
377
|
+
case FMT_BYTE: printf("%02x",*(uchar *)ValuePtr); s=1; break;
|
378
|
+
case FMT_USHORT: printf("%d",Get16u(ValuePtr)); s=2; break;
|
379
|
+
case FMT_ULONG:
|
380
|
+
case FMT_SLONG: printf("%d",Get32s(ValuePtr)); s=4; break;
|
381
|
+
case FMT_SSHORT: printf("%hd",(signed short)Get16u(ValuePtr)); s=2; break;
|
382
|
+
case FMT_URATIONAL:
|
383
|
+
case FMT_SRATIONAL:
|
384
|
+
printf("%d/%d",Get32s(ValuePtr), Get32s(4+(char *)ValuePtr));
|
385
|
+
s = 8;
|
386
|
+
break;
|
387
|
+
|
388
|
+
case FMT_SINGLE: printf("%f",(double)*(float *)ValuePtr); s=8; break;
|
389
|
+
case FMT_DOUBLE: printf("%f",*(double *)ValuePtr); s=8; break;
|
390
|
+
default:
|
391
|
+
printf("Unknown format %d:", Format);
|
392
|
+
return;
|
393
|
+
}
|
394
|
+
ByteCount -= s;
|
395
|
+
if (ByteCount <= 0) break;
|
396
|
+
printf(", ");
|
397
|
+
ValuePtr = (void *)((char *)ValuePtr + s);
|
398
|
+
|
399
|
+
}
|
400
|
+
if (n >= 16) printf("...");
|
401
|
+
}
|
402
|
+
|
403
|
+
|
404
|
+
//--------------------------------------------------------------------------
|
405
|
+
// Evaluate number, be it int, rational, or float from directory.
|
406
|
+
//--------------------------------------------------------------------------
|
407
|
+
double ConvertAnyFormat(void * ValuePtr, int Format)
|
408
|
+
{
|
409
|
+
double Value;
|
410
|
+
Value = 0;
|
411
|
+
|
412
|
+
switch(Format){
|
413
|
+
case FMT_SBYTE: Value = *(signed char *)ValuePtr; break;
|
414
|
+
case FMT_BYTE: Value = *(uchar *)ValuePtr; break;
|
415
|
+
|
416
|
+
case FMT_USHORT: Value = Get16u(ValuePtr); break;
|
417
|
+
case FMT_ULONG: Value = Get32u(ValuePtr); break;
|
418
|
+
|
419
|
+
case FMT_URATIONAL:
|
420
|
+
case FMT_SRATIONAL:
|
421
|
+
{
|
422
|
+
int Num,Den;
|
423
|
+
Num = Get32s(ValuePtr);
|
424
|
+
Den = Get32s(4+(char *)ValuePtr);
|
425
|
+
if (Den == 0){
|
426
|
+
Value = 0;
|
427
|
+
}else{
|
428
|
+
Value = (double)Num/Den;
|
429
|
+
}
|
430
|
+
break;
|
431
|
+
}
|
432
|
+
|
433
|
+
case FMT_SSHORT: Value = (signed short)Get16u(ValuePtr); break;
|
434
|
+
case FMT_SLONG: Value = Get32s(ValuePtr); break;
|
435
|
+
|
436
|
+
// Not sure if this is correct (never seen float used in Exif format)
|
437
|
+
case FMT_SINGLE: Value = (double)*(float *)ValuePtr; break;
|
438
|
+
case FMT_DOUBLE: Value = *(double *)ValuePtr; break;
|
439
|
+
|
440
|
+
default:
|
441
|
+
ErrNonfatal("Illegal format code %d in Exif header",Format,0);
|
442
|
+
}
|
443
|
+
return Value;
|
444
|
+
}
|
445
|
+
|
446
|
+
//--------------------------------------------------------------------------
|
447
|
+
// Process one of the nested EXIF directories.
|
448
|
+
//--------------------------------------------------------------------------
|
449
|
+
static void ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase,
|
450
|
+
unsigned ExifLength, int NestingLevel)
|
451
|
+
{
|
452
|
+
int de;
|
453
|
+
int a;
|
454
|
+
int NumDirEntries;
|
455
|
+
unsigned ThumbnailOffset = 0;
|
456
|
+
unsigned ThumbnailSize = 0;
|
457
|
+
char IndentString[25];
|
458
|
+
|
459
|
+
if (NestingLevel > 4){
|
460
|
+
ErrNonfatal("Maximum Exif directory nesting exceeded (corrupt Exif header)", 0,0);
|
461
|
+
return;
|
462
|
+
}
|
463
|
+
|
464
|
+
memset(IndentString, ' ', 25);
|
465
|
+
IndentString[NestingLevel * 4] = '\0';
|
466
|
+
|
467
|
+
|
468
|
+
NumDirEntries = Get16u(DirStart);
|
469
|
+
#define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry))
|
470
|
+
|
471
|
+
{
|
472
|
+
unsigned char * DirEnd;
|
473
|
+
DirEnd = DIR_ENTRY_ADDR(DirStart, NumDirEntries);
|
474
|
+
if (DirEnd+4 > (OffsetBase+ExifLength)){
|
475
|
+
if (DirEnd+2 == OffsetBase+ExifLength || DirEnd == OffsetBase+ExifLength){
|
476
|
+
// Version 1.3 of jhead would truncate a bit too much.
|
477
|
+
// This also caught later on as well.
|
478
|
+
}else{
|
479
|
+
ErrNonfatal("Illegally sized Exif subdirectory (%d entries)",NumDirEntries,0);
|
480
|
+
return;
|
481
|
+
}
|
482
|
+
}
|
483
|
+
if (DumpExifMap){
|
484
|
+
printf("Map: %05d-%05d: Directory\n",(int)(DirStart-OffsetBase), (int)(DirEnd+4-OffsetBase));
|
485
|
+
}
|
486
|
+
|
487
|
+
|
488
|
+
}
|
489
|
+
|
490
|
+
if (ShowTags){
|
491
|
+
printf("(dir has %d entries)\n",NumDirEntries);
|
492
|
+
}
|
493
|
+
|
494
|
+
for (de=0;de<NumDirEntries;de++){
|
495
|
+
int Tag, Format, Components;
|
496
|
+
unsigned char * ValuePtr;
|
497
|
+
int ByteCount;
|
498
|
+
unsigned char * DirEntry;
|
499
|
+
DirEntry = DIR_ENTRY_ADDR(DirStart, de);
|
500
|
+
|
501
|
+
Tag = Get16u(DirEntry);
|
502
|
+
Format = Get16u(DirEntry+2);
|
503
|
+
Components = Get32u(DirEntry+4);
|
504
|
+
|
505
|
+
if ((Format-1) >= NUM_FORMATS) {
|
506
|
+
// (-1) catches illegal zero case as unsigned underflows to positive large.
|
507
|
+
ErrNonfatal("Illegal number format %d for tag %04x in Exif", Format, Tag);
|
508
|
+
continue;
|
509
|
+
}
|
510
|
+
|
511
|
+
if ((unsigned)Components > 0x10000){
|
512
|
+
ErrNonfatal("Too many components %d for tag %04x in Exif", Components, Tag);
|
513
|
+
continue;
|
514
|
+
}
|
515
|
+
|
516
|
+
ByteCount = Components * BytesPerFormat[Format];
|
517
|
+
|
518
|
+
if (ByteCount > 4){
|
519
|
+
unsigned OffsetVal;
|
520
|
+
OffsetVal = Get32u(DirEntry+8);
|
521
|
+
// If its bigger than 4 bytes, the dir entry contains an offset.
|
522
|
+
if (OffsetVal+ByteCount > ExifLength){
|
523
|
+
// Bogus pointer offset and / or bytecount value
|
524
|
+
ErrNonfatal("Illegal value pointer for tag %04x in Exif", Tag,0);
|
525
|
+
continue;
|
526
|
+
}
|
527
|
+
ValuePtr = OffsetBase+OffsetVal;
|
528
|
+
|
529
|
+
if (OffsetVal > ImageInfo.LargestExifOffset){
|
530
|
+
ImageInfo.LargestExifOffset = OffsetVal;
|
531
|
+
}
|
532
|
+
|
533
|
+
if (DumpExifMap){
|
534
|
+
printf("Map: %05d-%05d: Data for tag %04x\n",OffsetVal, OffsetVal+ByteCount, Tag);
|
535
|
+
}
|
536
|
+
}else{
|
537
|
+
// 4 bytes or less and value is in the dir entry itself
|
538
|
+
ValuePtr = DirEntry+8;
|
539
|
+
}
|
540
|
+
|
541
|
+
if (Tag == TAG_MAKER_NOTE){
|
542
|
+
if (ShowTags){
|
543
|
+
printf("%s Maker note: ",IndentString);
|
544
|
+
}
|
545
|
+
ProcessMakerNote(ValuePtr, ByteCount, OffsetBase, ExifLength);
|
546
|
+
continue;
|
547
|
+
}
|
548
|
+
|
549
|
+
if (ShowTags){
|
550
|
+
// Show tag name
|
551
|
+
for (a=0;;a++){
|
552
|
+
if (a >= TAG_TABLE_SIZE){
|
553
|
+
printf(IndentString);
|
554
|
+
printf(" Unknown Tag %04x Value = ", Tag);
|
555
|
+
break;
|
556
|
+
}
|
557
|
+
if (TagTable[a].Tag == Tag){
|
558
|
+
printf(IndentString);
|
559
|
+
printf(" %s = ",TagTable[a].Desc);
|
560
|
+
break;
|
561
|
+
}
|
562
|
+
}
|
563
|
+
|
564
|
+
// Show tag value.
|
565
|
+
switch(Format){
|
566
|
+
case FMT_BYTE:
|
567
|
+
if(ByteCount>1){
|
568
|
+
printf("%.*ls\n", ByteCount/2, (wchar_t *)ValuePtr);
|
569
|
+
}else{
|
570
|
+
PrintFormatNumber(ValuePtr, Format, ByteCount);
|
571
|
+
printf("\n");
|
572
|
+
}
|
573
|
+
break;
|
574
|
+
|
575
|
+
case FMT_UNDEFINED:
|
576
|
+
// Undefined is typically an ascii string.
|
577
|
+
|
578
|
+
case FMT_STRING:
|
579
|
+
// String arrays printed without function call (different from int arrays)
|
580
|
+
{
|
581
|
+
int NoPrint = 0;
|
582
|
+
printf("\"");
|
583
|
+
for (a=0;a<ByteCount;a++){
|
584
|
+
if (ValuePtr[a] >= 32){
|
585
|
+
putchar(ValuePtr[a]);
|
586
|
+
NoPrint = 0;
|
587
|
+
}else{
|
588
|
+
// Avoiding indicating too many unprintable characters of proprietary
|
589
|
+
// bits of binary information this program may not know how to parse.
|
590
|
+
if (!NoPrint && a != ByteCount-1){
|
591
|
+
putchar('?');
|
592
|
+
NoPrint = 1;
|
593
|
+
}
|
594
|
+
}
|
595
|
+
}
|
596
|
+
printf("\"\n");
|
597
|
+
}
|
598
|
+
break;
|
599
|
+
|
600
|
+
default:
|
601
|
+
// Handle arrays of numbers later (will there ever be?)
|
602
|
+
PrintFormatNumber(ValuePtr, Format, ByteCount);
|
603
|
+
printf("\n");
|
604
|
+
}
|
605
|
+
}
|
606
|
+
|
607
|
+
// Extract useful components of tag
|
608
|
+
switch(Tag){
|
609
|
+
|
610
|
+
case TAG_MAKE:
|
611
|
+
strncpy(ImageInfo.CameraMake, (char *)ValuePtr, ByteCount < 31 ? ByteCount : 31);
|
612
|
+
break;
|
613
|
+
|
614
|
+
case TAG_MODEL:
|
615
|
+
strncpy(ImageInfo.CameraModel, (char *)ValuePtr, ByteCount < 39 ? ByteCount : 39);
|
616
|
+
break;
|
617
|
+
|
618
|
+
case TAG_DATETIME_ORIGINAL:
|
619
|
+
// If we get a DATETIME_ORIGINAL, we use that one.
|
620
|
+
strncpy(ImageInfo.DateTime, (char *)ValuePtr, 19);
|
621
|
+
// Fallthru...
|
622
|
+
|
623
|
+
case TAG_DATETIME_DIGITIZED:
|
624
|
+
case TAG_DATETIME:
|
625
|
+
if (!isdigit(ImageInfo.DateTime[0])){
|
626
|
+
// If we don't already have a DATETIME_ORIGINAL, use whatever
|
627
|
+
// time fields we may have.
|
628
|
+
strncpy(ImageInfo.DateTime, (char *)ValuePtr, 19);
|
629
|
+
}
|
630
|
+
|
631
|
+
if (ImageInfo.numDateTimeTags >= MAX_DATE_COPIES){
|
632
|
+
ErrNonfatal("More than %d date fields in Exif. This is nuts", MAX_DATE_COPIES, 0);
|
633
|
+
break;
|
634
|
+
}
|
635
|
+
ImageInfo.DateTimeOffsets[ImageInfo.numDateTimeTags++] =
|
636
|
+
(char *)ValuePtr - (char *)OffsetBase;
|
637
|
+
break;
|
638
|
+
|
639
|
+
case TAG_WINXP_COMMENT:
|
640
|
+
if (ImageInfo.Comments[0]){ // We already have a jpeg comment.
|
641
|
+
// Already have a comment (probably windows comment), skip this one.
|
642
|
+
if (ShowTags) printf("Windows XP commend and other comment in header\n");
|
643
|
+
break; // Already have a windows comment, skip this one.
|
644
|
+
}
|
645
|
+
|
646
|
+
if (ByteCount > 1){
|
647
|
+
if (ByteCount > MAX_COMMENT_SIZE) ByteCount = MAX_COMMENT_SIZE;
|
648
|
+
memcpy(ImageInfo.Comments, ValuePtr, ByteCount);
|
649
|
+
ImageInfo.CommentWidthchars = ByteCount/2;
|
650
|
+
}
|
651
|
+
break;
|
652
|
+
|
653
|
+
case TAG_USERCOMMENT:
|
654
|
+
if (ImageInfo.Comments[0]){ // We already have a jpeg comment.
|
655
|
+
// Already have a comment (probably windows comment), skip this one.
|
656
|
+
if (ShowTags) printf("Multiple comments in exif header\n");
|
657
|
+
break; // Already have a windows comment, skip this one.
|
658
|
+
}
|
659
|
+
|
660
|
+
// Comment is often padded with trailing spaces. Remove these first.
|
661
|
+
for (a=ByteCount;;){
|
662
|
+
a--;
|
663
|
+
if ((ValuePtr)[a] == ' '){
|
664
|
+
(ValuePtr)[a] = '\0';
|
665
|
+
}else{
|
666
|
+
break;
|
667
|
+
}
|
668
|
+
if (a == 0) break;
|
669
|
+
}
|
670
|
+
|
671
|
+
// Copy the comment
|
672
|
+
if (memcmp(ValuePtr, "ASCII",5) == 0){
|
673
|
+
for (a=5;a<10;a++){
|
674
|
+
int c;
|
675
|
+
c = (ValuePtr)[a];
|
676
|
+
if (c != '\0' && c != ' '){
|
677
|
+
strncpy(ImageInfo.Comments, (char *)ValuePtr+a, 199);
|
678
|
+
break;
|
679
|
+
}
|
680
|
+
}
|
681
|
+
}else{
|
682
|
+
strncpy(ImageInfo.Comments, (char *)ValuePtr, MAX_COMMENT_SIZE-1);
|
683
|
+
}
|
684
|
+
break;
|
685
|
+
|
686
|
+
case TAG_FNUMBER:
|
687
|
+
// Simplest way of expressing aperture, so I trust it the most.
|
688
|
+
// (overwrite previously computd value if there is one)
|
689
|
+
ImageInfo.ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format);
|
690
|
+
break;
|
691
|
+
|
692
|
+
case TAG_APERTURE:
|
693
|
+
case TAG_MAXAPERTURE:
|
694
|
+
// More relevant info always comes earlier, so only use this field if we don't
|
695
|
+
// have appropriate aperture information yet.
|
696
|
+
if (ImageInfo.ApertureFNumber == 0){
|
697
|
+
ImageInfo.ApertureFNumber
|
698
|
+
= (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2)*0.5);
|
699
|
+
}
|
700
|
+
break;
|
701
|
+
|
702
|
+
case TAG_FOCALLENGTH:
|
703
|
+
// Nice digital cameras actually save the focal length as a function
|
704
|
+
// of how farthey are zoomed in.
|
705
|
+
ImageInfo.FocalLength = (float)ConvertAnyFormat(ValuePtr, Format);
|
706
|
+
break;
|
707
|
+
|
708
|
+
case TAG_SUBJECT_DISTANCE:
|
709
|
+
// Inidcates the distacne the autofocus camera is focused to.
|
710
|
+
// Tends to be less accurate as distance increases.
|
711
|
+
ImageInfo.Distance = (float)ConvertAnyFormat(ValuePtr, Format);
|
712
|
+
break;
|
713
|
+
|
714
|
+
case TAG_EXPOSURETIME:
|
715
|
+
// Simplest way of expressing exposure time, so I trust it most.
|
716
|
+
// (overwrite previously computd value if there is one)
|
717
|
+
ImageInfo.ExposureTime = (float)ConvertAnyFormat(ValuePtr, Format);
|
718
|
+
break;
|
719
|
+
|
720
|
+
case TAG_SHUTTERSPEED:
|
721
|
+
// More complicated way of expressing exposure time, so only use
|
722
|
+
// this value if we don't already have it from somewhere else.
|
723
|
+
if (ImageInfo.ExposureTime == 0){
|
724
|
+
ImageInfo.ExposureTime
|
725
|
+
= (float)(1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2)));
|
726
|
+
}
|
727
|
+
break;
|
728
|
+
|
729
|
+
|
730
|
+
case TAG_FLASH:
|
731
|
+
ImageInfo.FlashUsed=(int)ConvertAnyFormat(ValuePtr, Format);
|
732
|
+
break;
|
733
|
+
|
734
|
+
case TAG_ORIENTATION:
|
735
|
+
if (NumOrientations >= 2){
|
736
|
+
// Can have another orientation tag for the thumbnail, but if there's
|
737
|
+
// a third one, things are stringae.
|
738
|
+
ErrNonfatal("More than two orientation in Exif",0,0);
|
739
|
+
break;
|
740
|
+
}
|
741
|
+
OrientationPtr[NumOrientations] = ValuePtr;
|
742
|
+
OrientationNumFormat[NumOrientations] = Format;
|
743
|
+
if (NumOrientations == 0){
|
744
|
+
ImageInfo.Orientation = (int)ConvertAnyFormat(ValuePtr, Format);
|
745
|
+
}
|
746
|
+
if (ImageInfo.Orientation < 0 || ImageInfo.Orientation > 8){
|
747
|
+
ErrNonfatal("Undefined rotation value %d in Exif", ImageInfo.Orientation, 0);
|
748
|
+
ImageInfo.Orientation = 0;
|
749
|
+
}
|
750
|
+
NumOrientations += 1;
|
751
|
+
break;
|
752
|
+
|
753
|
+
case TAG_PIXEL_Y_DIMENSION:
|
754
|
+
case TAG_PIXEL_X_DIMENSION:
|
755
|
+
// Use largest of height and width to deal with images that have been
|
756
|
+
// rotated to portrait format.
|
757
|
+
a = (int)ConvertAnyFormat(ValuePtr, Format);
|
758
|
+
if (ExifImageWidth < a) ExifImageWidth = a;
|
759
|
+
break;
|
760
|
+
|
761
|
+
case TAG_FOCAL_PLANE_XRES:
|
762
|
+
FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format);
|
763
|
+
break;
|
764
|
+
|
765
|
+
case TAG_FOCAL_PLANE_UNITS:
|
766
|
+
switch((int)ConvertAnyFormat(ValuePtr, Format)){
|
767
|
+
case 1: FocalplaneUnits = 25.4; break; // inch
|
768
|
+
case 2:
|
769
|
+
// According to the information I was using, 2 means meters.
|
770
|
+
// But looking at the Cannon powershot's files, inches is the only
|
771
|
+
// sensible value.
|
772
|
+
FocalplaneUnits = 25.4;
|
773
|
+
break;
|
774
|
+
|
775
|
+
case 3: FocalplaneUnits = 10; break; // centimeter
|
776
|
+
case 4: FocalplaneUnits = 1; break; // millimeter
|
777
|
+
case 5: FocalplaneUnits = .001; break; // micrometer
|
778
|
+
}
|
779
|
+
break;
|
780
|
+
|
781
|
+
case TAG_EXPOSURE_BIAS:
|
782
|
+
ImageInfo.ExposureBias = (float)ConvertAnyFormat(ValuePtr, Format);
|
783
|
+
break;
|
784
|
+
|
785
|
+
case TAG_WHITEBALANCE:
|
786
|
+
ImageInfo.Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format);
|
787
|
+
break;
|
788
|
+
|
789
|
+
case TAG_LIGHT_SOURCE:
|
790
|
+
ImageInfo.LightSource = (int)ConvertAnyFormat(ValuePtr, Format);
|
791
|
+
break;
|
792
|
+
|
793
|
+
case TAG_METERING_MODE:
|
794
|
+
ImageInfo.MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format);
|
795
|
+
break;
|
796
|
+
|
797
|
+
case TAG_EXPOSURE_PROGRAM:
|
798
|
+
ImageInfo.ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format);
|
799
|
+
break;
|
800
|
+
|
801
|
+
case TAG_EXPOSURE_INDEX:
|
802
|
+
if (ImageInfo.ISOequivalent == 0){
|
803
|
+
// Exposure index and ISO equivalent are often used interchangeably,
|
804
|
+
// so we will do the same in jhead.
|
805
|
+
// http://photography.about.com/library/glossary/bldef_ei.htm
|
806
|
+
ImageInfo.ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
|
807
|
+
}
|
808
|
+
break;
|
809
|
+
|
810
|
+
case TAG_EXPOSURE_MODE:
|
811
|
+
ImageInfo.ExposureMode = (int)ConvertAnyFormat(ValuePtr, Format);
|
812
|
+
break;
|
813
|
+
|
814
|
+
case TAG_ISO_EQUIVALENT:
|
815
|
+
ImageInfo.ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
|
816
|
+
if ( ImageInfo.ISOequivalent < 50 ){
|
817
|
+
// Fixes strange encoding on some older digicams.
|
818
|
+
ImageInfo.ISOequivalent *= 200;
|
819
|
+
}
|
820
|
+
break;
|
821
|
+
|
822
|
+
case TAG_DIGITALZOOMRATIO:
|
823
|
+
ImageInfo.DigitalZoomRatio = (float)ConvertAnyFormat(ValuePtr, Format);
|
824
|
+
break;
|
825
|
+
|
826
|
+
case TAG_THUMBNAIL_OFFSET:
|
827
|
+
ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format);
|
828
|
+
DirWithThumbnailPtrs = DirStart;
|
829
|
+
break;
|
830
|
+
|
831
|
+
case TAG_THUMBNAIL_LENGTH:
|
832
|
+
ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format);
|
833
|
+
ImageInfo.ThumbnailSizeOffset = ValuePtr-OffsetBase;
|
834
|
+
break;
|
835
|
+
|
836
|
+
case TAG_EXIF_OFFSET:
|
837
|
+
if (ShowTags) printf("%s Exif Dir:",IndentString);
|
838
|
+
|
839
|
+
case TAG_INTEROP_OFFSET:
|
840
|
+
if (Tag == TAG_INTEROP_OFFSET && ShowTags) printf("%s Interop Dir:",IndentString);
|
841
|
+
{
|
842
|
+
unsigned char * SubdirStart;
|
843
|
+
SubdirStart = OffsetBase + Get32u(ValuePtr);
|
844
|
+
if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){
|
845
|
+
ErrNonfatal("Illegal Exif or interop ofset directory link",0,0);
|
846
|
+
}else{
|
847
|
+
ProcessExifDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1);
|
848
|
+
}
|
849
|
+
continue;
|
850
|
+
}
|
851
|
+
break;
|
852
|
+
|
853
|
+
case TAG_GPSINFO:
|
854
|
+
if (ShowTags) printf("%s GPS info dir:",IndentString);
|
855
|
+
{
|
856
|
+
unsigned char * SubdirStart;
|
857
|
+
SubdirStart = OffsetBase + Get32u(ValuePtr);
|
858
|
+
if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){
|
859
|
+
ErrNonfatal("Illegal GPS directory link in Exif",0,0);
|
860
|
+
}else{
|
861
|
+
ProcessGpsInfo(SubdirStart, ByteCount, OffsetBase, ExifLength);
|
862
|
+
}
|
863
|
+
continue;
|
864
|
+
}
|
865
|
+
break;
|
866
|
+
|
867
|
+
case TAG_FOCALLENGTH_35MM:
|
868
|
+
// The focal length equivalent 35 mm is a 2.2 tag (defined as of April 2002)
|
869
|
+
// if its present, use it to compute equivalent focal length instead of
|
870
|
+
// computing it from sensor geometry and actual focal length.
|
871
|
+
ImageInfo.FocalLength35mmEquiv = (unsigned)ConvertAnyFormat(ValuePtr, Format);
|
872
|
+
break;
|
873
|
+
|
874
|
+
case TAG_DISTANCE_RANGE:
|
875
|
+
// Three possible standard values:
|
876
|
+
// 1 = macro, 2 = close, 3 = distant
|
877
|
+
ImageInfo.DistanceRange = (int)ConvertAnyFormat(ValuePtr, Format);
|
878
|
+
break;
|
879
|
+
|
880
|
+
|
881
|
+
|
882
|
+
case TAG_X_RESOLUTION:
|
883
|
+
if (NestingLevel==0) {// Only use the values from the top level directory
|
884
|
+
ImageInfo.xResolution = (float)ConvertAnyFormat(ValuePtr,Format);
|
885
|
+
// if yResolution has not been set, use the value of xResolution
|
886
|
+
if (ImageInfo.yResolution == 0.0) ImageInfo.yResolution = ImageInfo.xResolution;
|
887
|
+
}
|
888
|
+
break;
|
889
|
+
|
890
|
+
case TAG_Y_RESOLUTION:
|
891
|
+
if (NestingLevel==0) {// Only use the values from the top level directory
|
892
|
+
ImageInfo.yResolution = (float)ConvertAnyFormat(ValuePtr,Format);
|
893
|
+
// if xResolution has not been set, use the value of yResolution
|
894
|
+
if (ImageInfo.xResolution == 0.0) ImageInfo.xResolution = ImageInfo.yResolution;
|
895
|
+
}
|
896
|
+
break;
|
897
|
+
|
898
|
+
case TAG_RESOLUTION_UNIT:
|
899
|
+
if (NestingLevel==0) {// Only use the values from the top level directory
|
900
|
+
ImageInfo.ResolutionUnit = (int) ConvertAnyFormat(ValuePtr,Format);
|
901
|
+
}
|
902
|
+
break;
|
903
|
+
|
904
|
+
}
|
905
|
+
}
|
906
|
+
|
907
|
+
|
908
|
+
{
|
909
|
+
// In addition to linking to subdirectories via exif tags,
|
910
|
+
// there's also a potential link to another directory at the end of each
|
911
|
+
// directory. this has got to be the result of a committee!
|
912
|
+
unsigned char * SubdirStart;
|
913
|
+
unsigned Offset;
|
914
|
+
|
915
|
+
if (DIR_ENTRY_ADDR(DirStart, NumDirEntries) + 4 <= OffsetBase+ExifLength){
|
916
|
+
Offset = Get32u(DirStart+2+12*NumDirEntries);
|
917
|
+
if (Offset){
|
918
|
+
SubdirStart = OffsetBase + Offset;
|
919
|
+
if (SubdirStart > OffsetBase+ExifLength || SubdirStart < OffsetBase){
|
920
|
+
if (SubdirStart > OffsetBase && SubdirStart < OffsetBase+ExifLength+20){
|
921
|
+
// Jhead 1.3 or earlier would crop the whole directory!
|
922
|
+
// As Jhead produces this form of format incorrectness,
|
923
|
+
// I'll just let it pass silently
|
924
|
+
if (ShowTags) printf("Thumbnail removed with Jhead 1.3 or earlier\n");
|
925
|
+
}else{
|
926
|
+
ErrNonfatal("Illegal subdirectory link in Exif header",0,0);
|
927
|
+
}
|
928
|
+
}else{
|
929
|
+
if (SubdirStart <= OffsetBase+ExifLength){
|
930
|
+
if (ShowTags) printf("%s Continued directory ",IndentString);
|
931
|
+
ProcessExifDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1);
|
932
|
+
}
|
933
|
+
}
|
934
|
+
if (Offset > ImageInfo.LargestExifOffset){
|
935
|
+
ImageInfo.LargestExifOffset = Offset;
|
936
|
+
}
|
937
|
+
}
|
938
|
+
}else{
|
939
|
+
// The exif header ends before the last next directory pointer.
|
940
|
+
}
|
941
|
+
}
|
942
|
+
|
943
|
+
if (ThumbnailOffset){
|
944
|
+
ImageInfo.ThumbnailAtEnd = FALSE;
|
945
|
+
|
946
|
+
if (DumpExifMap){
|
947
|
+
printf("Map: %05d-%05d: Thumbnail\n",ThumbnailOffset, ThumbnailOffset+ThumbnailSize);
|
948
|
+
}
|
949
|
+
|
950
|
+
if (ThumbnailOffset <= ExifLength){
|
951
|
+
if (ThumbnailSize > ExifLength-ThumbnailOffset){
|
952
|
+
// If thumbnail extends past exif header, only save the part that
|
953
|
+
// actually exists. Canon's EOS viewer utility will do this - the
|
954
|
+
// thumbnail extracts ok with this hack.
|
955
|
+
ThumbnailSize = ExifLength-ThumbnailOffset;
|
956
|
+
if (ShowTags) printf("Thumbnail incorrectly placed in header\n");
|
957
|
+
|
958
|
+
}
|
959
|
+
// The thumbnail pointer appears to be valid. Store it.
|
960
|
+
ImageInfo.ThumbnailOffset = ThumbnailOffset;
|
961
|
+
ImageInfo.ThumbnailSize = ThumbnailSize;
|
962
|
+
|
963
|
+
if (ShowTags){
|
964
|
+
printf("Thumbnail size: %d bytes\n",ThumbnailSize);
|
965
|
+
}
|
966
|
+
}
|
967
|
+
}
|
968
|
+
}
|
969
|
+
|
970
|
+
|
971
|
+
//--------------------------------------------------------------------------
|
972
|
+
// Process a EXIF marker
|
973
|
+
// Describes all the drivel that most digital cameras include...
|
974
|
+
//--------------------------------------------------------------------------
|
975
|
+
void process_EXIF (unsigned char * ExifSection, unsigned int length)
|
976
|
+
{
|
977
|
+
unsigned int FirstOffset;
|
978
|
+
|
979
|
+
FocalplaneXRes = 0;
|
980
|
+
FocalplaneUnits = 0;
|
981
|
+
ExifImageWidth = 0;
|
982
|
+
NumOrientations = 0;
|
983
|
+
|
984
|
+
if (ShowTags){
|
985
|
+
printf("Exif header %d bytes long\n",length);
|
986
|
+
}
|
987
|
+
|
988
|
+
{ // Check the EXIF header component
|
989
|
+
static uchar ExifHeader[] = "Exif\0\0";
|
990
|
+
if (memcmp(ExifSection+2, ExifHeader,6)){
|
991
|
+
ErrNonfatal("Incorrect Exif header",0,0);
|
992
|
+
return;
|
993
|
+
}
|
994
|
+
}
|
995
|
+
|
996
|
+
if (memcmp(ExifSection+8,"II",2) == 0){
|
997
|
+
if (ShowTags) printf("Exif section in Intel order\n");
|
998
|
+
MotorolaOrder = 0;
|
999
|
+
}else{
|
1000
|
+
if (memcmp(ExifSection+8,"MM",2) == 0){
|
1001
|
+
if (ShowTags) printf("Exif section in Motorola order\n");
|
1002
|
+
MotorolaOrder = 1;
|
1003
|
+
}else{
|
1004
|
+
ErrNonfatal("Invalid Exif alignment marker.",0,0);
|
1005
|
+
return;
|
1006
|
+
}
|
1007
|
+
}
|
1008
|
+
|
1009
|
+
// Check the next value for correctness.
|
1010
|
+
if (Get16u(ExifSection+10) != 0x2a){
|
1011
|
+
ErrNonfatal("Invalid Exif start (1)",0,0);
|
1012
|
+
return;
|
1013
|
+
}
|
1014
|
+
|
1015
|
+
FirstOffset = Get32u(ExifSection+12);
|
1016
|
+
if (FirstOffset < 8 || FirstOffset > 16){
|
1017
|
+
if (FirstOffset < 16 || FirstOffset > length-16){
|
1018
|
+
ErrNonfatal("invalid offset for first Exif IFD value",0,0);
|
1019
|
+
return;
|
1020
|
+
}
|
1021
|
+
// Usually set to 8, but other values valid too.
|
1022
|
+
ErrNonfatal("Suspicious offset of first Exif IFD value",0,0);
|
1023
|
+
}
|
1024
|
+
|
1025
|
+
DirWithThumbnailPtrs = NULL;
|
1026
|
+
|
1027
|
+
|
1028
|
+
// First directory starts 16 bytes in. All offset are relative to 8 bytes in.
|
1029
|
+
ProcessExifDir(ExifSection+8+FirstOffset, ExifSection+8, length-8, 0);
|
1030
|
+
|
1031
|
+
ImageInfo.ThumbnailAtEnd = ImageInfo.ThumbnailOffset >= ImageInfo.LargestExifOffset ? TRUE : FALSE;
|
1032
|
+
|
1033
|
+
if (DumpExifMap){
|
1034
|
+
unsigned a,b;
|
1035
|
+
printf("Map: %05d- End of exif\n",length-8);
|
1036
|
+
for (a=0;a<length-8;a+= 10){
|
1037
|
+
printf("Map: %05d ",a);
|
1038
|
+
for (b=0;b<10;b++) printf(" %02x",*(ExifSection+8+a+b));
|
1039
|
+
printf("\n");
|
1040
|
+
}
|
1041
|
+
}
|
1042
|
+
|
1043
|
+
|
1044
|
+
// Compute the CCD width, in millimeters.
|
1045
|
+
if (FocalplaneXRes != 0){
|
1046
|
+
// Note: With some cameras, its not possible to compute this correctly because
|
1047
|
+
// they don't adjust the indicated focal plane resolution units when using less
|
1048
|
+
// than maximum resolution, so the CCDWidth value comes out too small. Nothing
|
1049
|
+
// that Jhad can do about it - its a camera problem.
|
1050
|
+
ImageInfo.CCDWidth = (float)(ExifImageWidth * FocalplaneUnits / FocalplaneXRes);
|
1051
|
+
|
1052
|
+
if (ImageInfo.FocalLength && ImageInfo.FocalLength35mmEquiv == 0){
|
1053
|
+
// Compute 35 mm equivalent focal length based on sensor geometry if we haven't
|
1054
|
+
// already got it explicitly from a tag.
|
1055
|
+
ImageInfo.FocalLength35mmEquiv = (int)(ImageInfo.FocalLength/ImageInfo.CCDWidth*36 + 0.5);
|
1056
|
+
}
|
1057
|
+
}
|
1058
|
+
}
|
1059
|
+
|
1060
|
+
|
1061
|
+
//--------------------------------------------------------------------------
|
1062
|
+
// Create minimal exif header - just date and thumbnail pointers,
|
1063
|
+
// so that date and thumbnail may be filled later.
|
1064
|
+
//--------------------------------------------------------------------------
|
1065
|
+
void create_EXIF(void)
|
1066
|
+
{
|
1067
|
+
char Buffer[256];
|
1068
|
+
|
1069
|
+
unsigned short NumEntries;
|
1070
|
+
int DataWriteIndex;
|
1071
|
+
int DateIndex;
|
1072
|
+
int DirIndex;
|
1073
|
+
int DirContinuation;
|
1074
|
+
|
1075
|
+
MotorolaOrder = 0;
|
1076
|
+
|
1077
|
+
memcpy(Buffer+2, "Exif\0\0II",8);
|
1078
|
+
Put16u(Buffer+10, 0x2a);
|
1079
|
+
|
1080
|
+
DataWriteIndex = 16;
|
1081
|
+
Put32u(Buffer+12, DataWriteIndex-8); // first IFD offset. Means start 16 bytes in.
|
1082
|
+
|
1083
|
+
{
|
1084
|
+
DirIndex = DataWriteIndex;
|
1085
|
+
NumEntries = 2;
|
1086
|
+
DataWriteIndex += 2 + NumEntries*12 + 4;
|
1087
|
+
|
1088
|
+
Put16u(Buffer+DirIndex, NumEntries); // Number of entries
|
1089
|
+
DirIndex += 2;
|
1090
|
+
|
1091
|
+
// Enitries go here...
|
1092
|
+
{
|
1093
|
+
// Date/time entry
|
1094
|
+
Put16u(Buffer+DirIndex, TAG_DATETIME); // Tag
|
1095
|
+
Put16u(Buffer+DirIndex + 2, FMT_STRING); // Format
|
1096
|
+
Put32u(Buffer+DirIndex + 4, 20); // Components
|
1097
|
+
Put32u(Buffer+DirIndex + 8, DataWriteIndex-8); // Pointer or value.
|
1098
|
+
DirIndex += 12;
|
1099
|
+
|
1100
|
+
DateIndex = DataWriteIndex;
|
1101
|
+
if (ImageInfo.numDateTimeTags){
|
1102
|
+
// If we had a pre-existing exif header, use time from that.
|
1103
|
+
memcpy(Buffer+DataWriteIndex, ImageInfo.DateTime, 19);
|
1104
|
+
Buffer[DataWriteIndex+19] = '\0';
|
1105
|
+
}else{
|
1106
|
+
// Oterwise, use the file's timestamp.
|
1107
|
+
FileTimeAsString(Buffer+DataWriteIndex);
|
1108
|
+
}
|
1109
|
+
DataWriteIndex += 20;
|
1110
|
+
|
1111
|
+
// Link to exif dir entry
|
1112
|
+
Put16u(Buffer+DirIndex, TAG_EXIF_OFFSET); // Tag
|
1113
|
+
Put16u(Buffer+DirIndex + 2, FMT_ULONG); // Format
|
1114
|
+
Put32u(Buffer+DirIndex + 4, 1); // Components
|
1115
|
+
Put32u(Buffer+DirIndex + 8, DataWriteIndex-8); // Pointer or value.
|
1116
|
+
DirIndex += 12;
|
1117
|
+
}
|
1118
|
+
|
1119
|
+
// End of directory - contains optional link to continued directory.
|
1120
|
+
DirContinuation = DirIndex;
|
1121
|
+
}
|
1122
|
+
|
1123
|
+
{
|
1124
|
+
DirIndex = DataWriteIndex;
|
1125
|
+
NumEntries = 1;
|
1126
|
+
DataWriteIndex += 2 + NumEntries*12 + 4;
|
1127
|
+
|
1128
|
+
Put16u(Buffer+DirIndex, NumEntries); // Number of entries
|
1129
|
+
DirIndex += 2;
|
1130
|
+
|
1131
|
+
// Original date/time entry
|
1132
|
+
Put16u(Buffer+DirIndex, TAG_DATETIME_ORIGINAL); // Tag
|
1133
|
+
Put16u(Buffer+DirIndex + 2, FMT_STRING); // Format
|
1134
|
+
Put32u(Buffer+DirIndex + 4, 20); // Components
|
1135
|
+
Put32u(Buffer+DirIndex + 8, DataWriteIndex-8); // Pointer or value.
|
1136
|
+
DirIndex += 12;
|
1137
|
+
|
1138
|
+
memcpy(Buffer+DataWriteIndex, Buffer+DateIndex, 20);
|
1139
|
+
DataWriteIndex += 20;
|
1140
|
+
|
1141
|
+
// End of directory - contains optional link to continued directory.
|
1142
|
+
Put32u(Buffer+DirIndex, 0);
|
1143
|
+
}
|
1144
|
+
|
1145
|
+
{
|
1146
|
+
//Continuation which links to this directory;
|
1147
|
+
Put32u(Buffer+DirContinuation, DataWriteIndex-8);
|
1148
|
+
DirIndex = DataWriteIndex;
|
1149
|
+
NumEntries = 2;
|
1150
|
+
DataWriteIndex += 2 + NumEntries*12 + 4;
|
1151
|
+
|
1152
|
+
Put16u(Buffer+DirIndex, NumEntries); // Number of entries
|
1153
|
+
DirIndex += 2;
|
1154
|
+
{
|
1155
|
+
// Link to exif dir entry
|
1156
|
+
Put16u(Buffer+DirIndex, TAG_THUMBNAIL_OFFSET); // Tag
|
1157
|
+
Put16u(Buffer+DirIndex + 2, FMT_ULONG); // Format
|
1158
|
+
Put32u(Buffer+DirIndex + 4, 1); // Components
|
1159
|
+
Put32u(Buffer+DirIndex + 8, DataWriteIndex-8); // Pointer or value.
|
1160
|
+
DirIndex += 12;
|
1161
|
+
}
|
1162
|
+
|
1163
|
+
{
|
1164
|
+
// Link to exif dir entry
|
1165
|
+
Put16u(Buffer+DirIndex, TAG_THUMBNAIL_LENGTH); // Tag
|
1166
|
+
Put16u(Buffer+DirIndex + 2, FMT_ULONG); // Format
|
1167
|
+
Put32u(Buffer+DirIndex + 4, 1); // Components
|
1168
|
+
Put32u(Buffer+DirIndex + 8, 0); // Pointer or value.
|
1169
|
+
DirIndex += 12;
|
1170
|
+
}
|
1171
|
+
|
1172
|
+
// End of directory - contains optional link to continued directory.
|
1173
|
+
Put32u(Buffer+DirIndex, 0);
|
1174
|
+
}
|
1175
|
+
|
1176
|
+
|
1177
|
+
Buffer[0] = (unsigned char)(DataWriteIndex >> 8);
|
1178
|
+
Buffer[1] = (unsigned char)DataWriteIndex;
|
1179
|
+
|
1180
|
+
// Remove old exif section, if there was one.
|
1181
|
+
RemoveSectionType(M_EXIF);
|
1182
|
+
|
1183
|
+
{
|
1184
|
+
// Sections need malloced buffers, so do that now, especially because
|
1185
|
+
// we now know how big it needs to be allocated.
|
1186
|
+
unsigned char * NewBuf = malloc(DataWriteIndex);
|
1187
|
+
if (NewBuf == NULL){
|
1188
|
+
ErrFatal("Could not allocate memory");
|
1189
|
+
}
|
1190
|
+
memcpy(NewBuf, Buffer, DataWriteIndex);
|
1191
|
+
|
1192
|
+
CreateSection(M_EXIF, NewBuf, DataWriteIndex);
|
1193
|
+
|
1194
|
+
// Re-parse new exif section, now that its in place
|
1195
|
+
// otherwise, we risk touching data that has already been freed.
|
1196
|
+
process_EXIF(NewBuf, DataWriteIndex);
|
1197
|
+
}
|
1198
|
+
}
|
1199
|
+
|
1200
|
+
//--------------------------------------------------------------------------
|
1201
|
+
// Cler the rotation tag in the exif header to 1.
|
1202
|
+
//--------------------------------------------------------------------------
|
1203
|
+
const char * ClearOrientation(void)
|
1204
|
+
{
|
1205
|
+
int a;
|
1206
|
+
if (NumOrientations == 0) return NULL;
|
1207
|
+
|
1208
|
+
for (a=0;a<NumOrientations;a++){
|
1209
|
+
switch(OrientationNumFormat[a]){
|
1210
|
+
case FMT_SBYTE:
|
1211
|
+
case FMT_BYTE:
|
1212
|
+
*(uchar *)(OrientationPtr[a]) = 1;
|
1213
|
+
break;
|
1214
|
+
|
1215
|
+
case FMT_USHORT:
|
1216
|
+
Put16u(OrientationPtr[a], 1);
|
1217
|
+
break;
|
1218
|
+
|
1219
|
+
case FMT_ULONG:
|
1220
|
+
case FMT_SLONG:
|
1221
|
+
memset(OrientationPtr, 0, 4);
|
1222
|
+
// Can't be bothered to write generic Put32 if I only use it once.
|
1223
|
+
if (MotorolaOrder){
|
1224
|
+
((uchar *)OrientationPtr[a])[3] = 1;
|
1225
|
+
}else{
|
1226
|
+
((uchar *)OrientationPtr[a])[0] = 1;
|
1227
|
+
}
|
1228
|
+
break;
|
1229
|
+
|
1230
|
+
default:
|
1231
|
+
return NULL;
|
1232
|
+
}
|
1233
|
+
}
|
1234
|
+
|
1235
|
+
return OrientTab[ImageInfo.Orientation];
|
1236
|
+
}
|
1237
|
+
|
1238
|
+
|
1239
|
+
|
1240
|
+
//--------------------------------------------------------------------------
|
1241
|
+
// Remove thumbnail out of the exif image.
|
1242
|
+
//--------------------------------------------------------------------------
|
1243
|
+
int RemoveThumbnail(unsigned char * ExifSection)
|
1244
|
+
{
|
1245
|
+
if (!DirWithThumbnailPtrs ||
|
1246
|
+
ImageInfo.ThumbnailOffset == 0 ||
|
1247
|
+
ImageInfo.ThumbnailSize == 0){
|
1248
|
+
// No thumbnail, or already deleted it.
|
1249
|
+
return 0;
|
1250
|
+
}
|
1251
|
+
if (ImageInfo.ThumbnailAtEnd == FALSE){
|
1252
|
+
ErrNonfatal("Thumbnail not at end of Exif header, can't remove it", 0, 0);
|
1253
|
+
return 0;
|
1254
|
+
}
|
1255
|
+
|
1256
|
+
{
|
1257
|
+
int de;
|
1258
|
+
int NumDirEntries;
|
1259
|
+
NumDirEntries = Get16u(DirWithThumbnailPtrs);
|
1260
|
+
|
1261
|
+
for (de=0;de<NumDirEntries;de++){
|
1262
|
+
int Tag;
|
1263
|
+
unsigned char * DirEntry;
|
1264
|
+
DirEntry = DIR_ENTRY_ADDR(DirWithThumbnailPtrs, de);
|
1265
|
+
Tag = Get16u(DirEntry);
|
1266
|
+
if (Tag == TAG_THUMBNAIL_LENGTH){
|
1267
|
+
// Set length to zero.
|
1268
|
+
if (Get16u(DirEntry+2) != FMT_ULONG){
|
1269
|
+
// non standard format encoding. Can't do it.
|
1270
|
+
ErrNonfatal("Can't remove Exif thumbnail", 0, 0);
|
1271
|
+
return 0;
|
1272
|
+
}
|
1273
|
+
Put32u(DirEntry+8, 0);
|
1274
|
+
}
|
1275
|
+
}
|
1276
|
+
}
|
1277
|
+
|
1278
|
+
// This is how far the non thumbnail data went.
|
1279
|
+
return ImageInfo.ThumbnailOffset+8;
|
1280
|
+
|
1281
|
+
}
|
1282
|
+
|
1283
|
+
|
1284
|
+
//--------------------------------------------------------------------------
|
1285
|
+
// Convert exif time to Unix time structure
|
1286
|
+
//--------------------------------------------------------------------------
|
1287
|
+
int Exif2tm(struct tm * timeptr, char * ExifTime)
|
1288
|
+
{
|
1289
|
+
int a;
|
1290
|
+
|
1291
|
+
timeptr->tm_wday = -1;
|
1292
|
+
|
1293
|
+
// Check for format: YYYY:MM:DD HH:MM:SS format.
|
1294
|
+
// Date and time normally separated by a space, but also seen a ':' there, so
|
1295
|
+
// skip the middle space with '%*c' so it can be any character.
|
1296
|
+
timeptr->tm_sec = 0;
|
1297
|
+
a = sscanf(ExifTime, "%d%*c%d%*c%d%*c%d:%d:%d",
|
1298
|
+
&timeptr->tm_year, &timeptr->tm_mon, &timeptr->tm_mday,
|
1299
|
+
&timeptr->tm_hour, &timeptr->tm_min, &timeptr->tm_sec);
|
1300
|
+
|
1301
|
+
if (a >= 5){
|
1302
|
+
if (timeptr->tm_year <= 12 && timeptr->tm_mday > 2000 && ExifTime[2] == '.'){
|
1303
|
+
// LG Electronics VX-9700 seems to encode the date as 'MM.DD.YYYY HH:MM'
|
1304
|
+
// can't these people read the standard? At least they left enough room
|
1305
|
+
// in the header to put an Exif format date in there.
|
1306
|
+
int tmp;
|
1307
|
+
tmp = timeptr->tm_year;
|
1308
|
+
timeptr->tm_year = timeptr->tm_mday;
|
1309
|
+
timeptr->tm_mday = timeptr->tm_mon;
|
1310
|
+
timeptr->tm_mon = tmp;
|
1311
|
+
}
|
1312
|
+
|
1313
|
+
// Accept five or six parameters. Some cameras do not store seconds.
|
1314
|
+
timeptr->tm_isdst = -1;
|
1315
|
+
timeptr->tm_mon -= 1; // Adjust for unix zero-based months
|
1316
|
+
timeptr->tm_year -= 1900; // Adjust for year starting at 1900
|
1317
|
+
return TRUE; // worked.
|
1318
|
+
}
|
1319
|
+
|
1320
|
+
return FALSE; // Wasn't in Exif date format.
|
1321
|
+
}
|
1322
|
+
|
1323
|
+
|
1324
|
+
//--------------------------------------------------------------------------
|
1325
|
+
// Show the collected image info, displaying camera F-stop and shutter speed
|
1326
|
+
// in a consistent and legible fashion.
|
1327
|
+
//--------------------------------------------------------------------------
|
1328
|
+
void ShowImageInfo(int ShowFileInfo)
|
1329
|
+
{
|
1330
|
+
if (ShowFileInfo){
|
1331
|
+
printf("File name : %s\n",ImageInfo.FileName);
|
1332
|
+
printf("File size : %d bytes\n",ImageInfo.FileSize);
|
1333
|
+
|
1334
|
+
{
|
1335
|
+
char Temp[20];
|
1336
|
+
FileTimeAsString(Temp);
|
1337
|
+
printf("File date : %s\n",Temp);
|
1338
|
+
}
|
1339
|
+
}
|
1340
|
+
|
1341
|
+
if (ImageInfo.CameraMake[0]){
|
1342
|
+
printf("Camera make : %s\n",ImageInfo.CameraMake);
|
1343
|
+
printf("Camera model : %s\n",ImageInfo.CameraModel);
|
1344
|
+
}
|
1345
|
+
if (ImageInfo.DateTime[0]){
|
1346
|
+
printf("Date/Time : %s\n",ImageInfo.DateTime);
|
1347
|
+
}
|
1348
|
+
printf("Resolution : %d x %d\n",ImageInfo.Width, ImageInfo.Height);
|
1349
|
+
|
1350
|
+
if (ImageInfo.Orientation > 1){
|
1351
|
+
// Only print orientation if one was supplied, and if its not 1 (normal orientation)
|
1352
|
+
printf("Orientation : %s\n", OrientTab[ImageInfo.Orientation]);
|
1353
|
+
}
|
1354
|
+
|
1355
|
+
if (ImageInfo.IsColor == 0){
|
1356
|
+
printf("Color/bw : Black and white\n");
|
1357
|
+
}
|
1358
|
+
|
1359
|
+
if (ImageInfo.FlashUsed >= 0){
|
1360
|
+
if (ImageInfo.FlashUsed & 1){
|
1361
|
+
printf("Flash used : Yes");
|
1362
|
+
switch (ImageInfo.FlashUsed){
|
1363
|
+
case 0x5: printf(" (Strobe light not detected)"); break;
|
1364
|
+
case 0x7: printf(" (Strobe light detected) "); break;
|
1365
|
+
case 0x9: printf(" (manual)"); break;
|
1366
|
+
case 0xd: printf(" (manual, return light not detected)"); break;
|
1367
|
+
case 0xf: printf(" (manual, return light detected)"); break;
|
1368
|
+
case 0x19:printf(" (auto)"); break;
|
1369
|
+
case 0x1d:printf(" (auto, return light not detected)"); break;
|
1370
|
+
case 0x1f:printf(" (auto, return light detected)"); break;
|
1371
|
+
case 0x41:printf(" (red eye reduction mode)"); break;
|
1372
|
+
case 0x45:printf(" (red eye reduction mode return light not detected)"); break;
|
1373
|
+
case 0x47:printf(" (red eye reduction mode return light detected)"); break;
|
1374
|
+
case 0x49:printf(" (manual, red eye reduction mode)"); break;
|
1375
|
+
case 0x4d:printf(" (manual, red eye reduction mode, return light not detected)"); break;
|
1376
|
+
case 0x4f:printf(" (red eye reduction mode, return light detected)"); break;
|
1377
|
+
case 0x59:printf(" (auto, red eye reduction mode)"); break;
|
1378
|
+
case 0x5d:printf(" (auto, red eye reduction mode, return light not detected)"); break;
|
1379
|
+
case 0x5f:printf(" (auto, red eye reduction mode, return light detected)"); break;
|
1380
|
+
}
|
1381
|
+
}else{
|
1382
|
+
printf("Flash used : No");
|
1383
|
+
switch (ImageInfo.FlashUsed){
|
1384
|
+
case 0x18:printf(" (auto)"); break;
|
1385
|
+
}
|
1386
|
+
}
|
1387
|
+
printf("\n");
|
1388
|
+
}
|
1389
|
+
|
1390
|
+
|
1391
|
+
if (ImageInfo.FocalLength){
|
1392
|
+
printf("Focal length : %4.1fmm",(double)ImageInfo.FocalLength);
|
1393
|
+
if (ImageInfo.FocalLength35mmEquiv){
|
1394
|
+
printf(" (35mm equivalent: %dmm)", ImageInfo.FocalLength35mmEquiv);
|
1395
|
+
}
|
1396
|
+
printf("\n");
|
1397
|
+
}
|
1398
|
+
|
1399
|
+
if (ImageInfo.DigitalZoomRatio > 1){
|
1400
|
+
// Digital zoom used. Shame on you!
|
1401
|
+
printf("Digital Zoom : %1.3fx\n", (double)ImageInfo.DigitalZoomRatio);
|
1402
|
+
}
|
1403
|
+
|
1404
|
+
if (ImageInfo.CCDWidth){
|
1405
|
+
printf("CCD width : %4.2fmm\n",(double)ImageInfo.CCDWidth);
|
1406
|
+
}
|
1407
|
+
|
1408
|
+
if (ImageInfo.ExposureTime){
|
1409
|
+
if (ImageInfo.ExposureTime < 0.010){
|
1410
|
+
printf("Exposure time: %6.4f s ",(double)ImageInfo.ExposureTime);
|
1411
|
+
}else{
|
1412
|
+
printf("Exposure time: %5.3f s ",(double)ImageInfo.ExposureTime);
|
1413
|
+
}
|
1414
|
+
if (ImageInfo.ExposureTime <= 0.5){
|
1415
|
+
printf(" (1/%d)",(int)(0.5 + 1/ImageInfo.ExposureTime));
|
1416
|
+
}
|
1417
|
+
printf("\n");
|
1418
|
+
}
|
1419
|
+
if (ImageInfo.ApertureFNumber){
|
1420
|
+
printf("Aperture : f/%3.1f\n",(double)ImageInfo.ApertureFNumber);
|
1421
|
+
}
|
1422
|
+
if (ImageInfo.Distance){
|
1423
|
+
if (ImageInfo.Distance < 0){
|
1424
|
+
printf("Focus dist. : Infinite\n");
|
1425
|
+
}else{
|
1426
|
+
printf("Focus dist. : %4.2fm\n",(double)ImageInfo.Distance);
|
1427
|
+
}
|
1428
|
+
}
|
1429
|
+
|
1430
|
+
if (ImageInfo.ISOequivalent){
|
1431
|
+
printf("ISO equiv. : %2d\n",(int)ImageInfo.ISOequivalent);
|
1432
|
+
}
|
1433
|
+
|
1434
|
+
if (ImageInfo.ExposureBias){
|
1435
|
+
// If exposure bias was specified, but set to zero, presumably its no bias at all,
|
1436
|
+
// so only show it if its nonzero.
|
1437
|
+
printf("Exposure bias: %4.2f\n",(double)ImageInfo.ExposureBias);
|
1438
|
+
}
|
1439
|
+
|
1440
|
+
switch(ImageInfo.Whitebalance) {
|
1441
|
+
case 1:
|
1442
|
+
printf("Whitebalance : Manual\n");
|
1443
|
+
break;
|
1444
|
+
case 0:
|
1445
|
+
printf("Whitebalance : Auto\n");
|
1446
|
+
break;
|
1447
|
+
}
|
1448
|
+
|
1449
|
+
//Quercus: 17-1-2004 Added LightSource, some cams return this, whitebalance or both
|
1450
|
+
switch(ImageInfo.LightSource) {
|
1451
|
+
case 1:
|
1452
|
+
printf("Light Source : Daylight\n");
|
1453
|
+
break;
|
1454
|
+
case 2:
|
1455
|
+
printf("Light Source : Fluorescent\n");
|
1456
|
+
break;
|
1457
|
+
case 3:
|
1458
|
+
printf("Light Source : Incandescent\n");
|
1459
|
+
break;
|
1460
|
+
case 4:
|
1461
|
+
printf("Light Source : Flash\n");
|
1462
|
+
break;
|
1463
|
+
case 9:
|
1464
|
+
printf("Light Source : Fine weather\n");
|
1465
|
+
break;
|
1466
|
+
case 11:
|
1467
|
+
printf("Light Source : Shade\n");
|
1468
|
+
break;
|
1469
|
+
default:; //Quercus: 17-1-2004 There are many more modes for this, check Exif2.2 specs
|
1470
|
+
// If it just says 'unknown' or we don't know it, then
|
1471
|
+
// don't bother showing it - it doesn't add any useful information.
|
1472
|
+
}
|
1473
|
+
|
1474
|
+
if (ImageInfo.MeteringMode > 0){ // 05-jan-2001 vcs
|
1475
|
+
printf("Metering Mode: ");
|
1476
|
+
switch(ImageInfo.MeteringMode) {
|
1477
|
+
case 1: printf("average\n"); break;
|
1478
|
+
case 2: printf("center weight\n"); break;
|
1479
|
+
case 3: printf("spot\n"); break;
|
1480
|
+
case 4: printf("multi spot\n"); break;
|
1481
|
+
case 5: printf("pattern\n"); break;
|
1482
|
+
case 6: printf("partial\n"); break;
|
1483
|
+
case 255: printf("other\n"); break;
|
1484
|
+
default: printf("unknown (%d)\n",ImageInfo.MeteringMode); break;
|
1485
|
+
}
|
1486
|
+
}
|
1487
|
+
|
1488
|
+
if (ImageInfo.ExposureProgram){ // 05-jan-2001 vcs
|
1489
|
+
switch(ImageInfo.ExposureProgram) {
|
1490
|
+
case 1:
|
1491
|
+
printf("Exposure : Manual\n");
|
1492
|
+
break;
|
1493
|
+
case 2:
|
1494
|
+
printf("Exposure : program (auto)\n");
|
1495
|
+
break;
|
1496
|
+
case 3:
|
1497
|
+
printf("Exposure : aperture priority (semi-auto)\n");
|
1498
|
+
break;
|
1499
|
+
case 4:
|
1500
|
+
printf("Exposure : shutter priority (semi-auto)\n");
|
1501
|
+
break;
|
1502
|
+
case 5:
|
1503
|
+
printf("Exposure : Creative Program (based towards depth of field)\n");
|
1504
|
+
break;
|
1505
|
+
case 6:
|
1506
|
+
printf("Exposure : Action program (based towards fast shutter speed)\n");
|
1507
|
+
break;
|
1508
|
+
case 7:
|
1509
|
+
printf("Exposure : Portrait Mode\n");
|
1510
|
+
break;
|
1511
|
+
case 8:
|
1512
|
+
printf("Exposure : LandscapeMode \n");
|
1513
|
+
break;
|
1514
|
+
default:
|
1515
|
+
break;
|
1516
|
+
}
|
1517
|
+
}
|
1518
|
+
switch(ImageInfo.ExposureMode){
|
1519
|
+
case 0: // Automatic (not worth cluttering up output for)
|
1520
|
+
break;
|
1521
|
+
case 1: printf("Exposure Mode: Manual\n");
|
1522
|
+
break;
|
1523
|
+
case 2: printf("Exposure Mode: Auto bracketing\n");
|
1524
|
+
break;
|
1525
|
+
}
|
1526
|
+
|
1527
|
+
if (ImageInfo.DistanceRange) {
|
1528
|
+
printf("Focus range : ");
|
1529
|
+
switch(ImageInfo.DistanceRange) {
|
1530
|
+
case 1:
|
1531
|
+
printf("macro");
|
1532
|
+
break;
|
1533
|
+
case 2:
|
1534
|
+
printf("close");
|
1535
|
+
break;
|
1536
|
+
case 3:
|
1537
|
+
printf("distant");
|
1538
|
+
break;
|
1539
|
+
}
|
1540
|
+
printf("\n");
|
1541
|
+
}
|
1542
|
+
|
1543
|
+
|
1544
|
+
|
1545
|
+
if (ImageInfo.Process != M_SOF0){
|
1546
|
+
// don't show it if its the plain old boring 'baseline' process, but do
|
1547
|
+
// show it if its something else, like 'progressive' (used on web sometimes)
|
1548
|
+
int a;
|
1549
|
+
for (a=0;;a++){
|
1550
|
+
if (a >= PROCESS_TABLE_SIZE){
|
1551
|
+
// ran off the end of the table.
|
1552
|
+
printf("Jpeg process : Unknown\n");
|
1553
|
+
break;
|
1554
|
+
}
|
1555
|
+
if (ProcessTable[a].Tag == ImageInfo.Process){
|
1556
|
+
printf("Jpeg process : %s\n",ProcessTable[a].Desc);
|
1557
|
+
break;
|
1558
|
+
}
|
1559
|
+
}
|
1560
|
+
}
|
1561
|
+
|
1562
|
+
if (ImageInfo.GpsInfoPresent){
|
1563
|
+
printf("GPS Latitude : %s\n",ImageInfo.GpsLat);
|
1564
|
+
printf("GPS Longitude: %s\n",ImageInfo.GpsLong);
|
1565
|
+
if (ImageInfo.GpsAlt[0]) printf("GPS Altitude : %s\n",ImageInfo.GpsAlt);
|
1566
|
+
}
|
1567
|
+
|
1568
|
+
// Print the comment. Print 'Comment:' for each new line of comment.
|
1569
|
+
if (ImageInfo.Comments[0]){
|
1570
|
+
int a,c;
|
1571
|
+
printf("Comment : ");
|
1572
|
+
if (!ImageInfo.CommentWidthchars){
|
1573
|
+
for (a=0;a<MAX_COMMENT_SIZE;a++){
|
1574
|
+
c = ImageInfo.Comments[a];
|
1575
|
+
if (c == '\0') break;
|
1576
|
+
if (c == '\n'){
|
1577
|
+
// Do not start a new line if the string ends with a carriage return.
|
1578
|
+
if (ImageInfo.Comments[a+1] != '\0'){
|
1579
|
+
printf("\nComment : ");
|
1580
|
+
}else{
|
1581
|
+
printf("\n");
|
1582
|
+
}
|
1583
|
+
}else{
|
1584
|
+
putchar(c);
|
1585
|
+
}
|
1586
|
+
}
|
1587
|
+
printf("\n");
|
1588
|
+
}else{
|
1589
|
+
printf("%.*ls\n", ImageInfo.CommentWidthchars, (wchar_t *)ImageInfo.Comments);
|
1590
|
+
}
|
1591
|
+
}
|
1592
|
+
}
|
1593
|
+
|
1594
|
+
|
1595
|
+
//--------------------------------------------------------------------------
|
1596
|
+
// Summarize highlights of image info on one line (suitable for grep-ing)
|
1597
|
+
//--------------------------------------------------------------------------
|
1598
|
+
void ShowConciseImageInfo(void)
|
1599
|
+
{
|
1600
|
+
printf("\"%s\"",ImageInfo.FileName);
|
1601
|
+
|
1602
|
+
printf(" %dx%d",ImageInfo.Width, ImageInfo.Height);
|
1603
|
+
|
1604
|
+
if (ImageInfo.ExposureTime){
|
1605
|
+
if (ImageInfo.ExposureTime <= 0.5){
|
1606
|
+
printf(" (1/%d)",(int)(0.5 + 1/ImageInfo.ExposureTime));
|
1607
|
+
}else{
|
1608
|
+
printf(" (%1.1f)",ImageInfo.ExposureTime);
|
1609
|
+
}
|
1610
|
+
}
|
1611
|
+
|
1612
|
+
if (ImageInfo.ApertureFNumber){
|
1613
|
+
printf(" f/%3.1f",(double)ImageInfo.ApertureFNumber);
|
1614
|
+
}
|
1615
|
+
|
1616
|
+
if (ImageInfo.FocalLength35mmEquiv){
|
1617
|
+
printf(" f(35)=%dmm",ImageInfo.FocalLength35mmEquiv);
|
1618
|
+
}
|
1619
|
+
|
1620
|
+
if (ImageInfo.FlashUsed >= 0 && ImageInfo.FlashUsed & 1){
|
1621
|
+
printf(" (flash)");
|
1622
|
+
}
|
1623
|
+
|
1624
|
+
if (ImageInfo.IsColor == 0){
|
1625
|
+
printf(" (bw)");
|
1626
|
+
}
|
1627
|
+
|
1628
|
+
printf("\n");
|
1629
|
+
}
|