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