rjhead 2.88.1 → 2.88.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +0 -1
- data/VERSION +1 -1
- data/bin/jhead +0 -0
- data/rjhead.gemspec +4 -21
- metadata +6 -24
- data/ext/changes.txt +0 -351
- data/ext/exif.c +0 -1629
- data/ext/extconf.rb +0 -0
- data/ext/gpsinfo.c +0 -217
- data/ext/iptc.c +0 -205
- data/ext/jhead.1 +0 -409
- data/ext/jhead.c +0 -1697
- data/ext/jhead.h +0 -251
- data/ext/jhead.spec +0 -149
- data/ext/jpgfile.c +0 -738
- data/ext/make.bat +0 -1
- data/ext/makefile +0 -23
- data/ext/makefile-win32 +0 -27
- data/ext/makernote.c +0 -184
- data/ext/myglob.c +0 -305
- data/ext/paths.c +0 -140
- data/ext/readme.txt +0 -60
- data/ext/usage.html +0 -469
- data/setup.rake +0 -1
data/ext/exif.c
DELETED
@@ -1,1629 +0,0 @@
|
|
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
|
-
}
|