blob: 381d9dc9d4c68279c9ba7cb387d8644bd0079527 [file] [log] [blame]
The Android Open Source Project34a25642009-03-03 19:30:03 -08001//--------------------------------------------------------------------------
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#include <ctype.h>
13#include <utils/Log.h>
14
15static unsigned char * DirWithThumbnailPtrs;
16static double FocalplaneXRes;
17static double FocalplaneUnits;
18static int ExifImageWidth;
19static int MotorolaOrder = 0;
20
21// for fixing the rotation.
22static void * OrientationPtr[2];
23static int OrientationNumFormat[2];
24int NumOrientations = 0;
25
26
27// Define the line below to turn on poor man's debugging output
28#undef SUPERDEBUG
29
30#ifdef SUPERDEBUG
Steve Block7a314da2012-01-06 19:10:19 +000031#define printf ALOGE
The Android Open Source Project34a25642009-03-03 19:30:03 -080032#endif
33
34//--------------------------------------------------------------------------
35// Table of Jpeg encoding process names
36static const TagTable_t ProcessTable[] = {
37 { M_SOF0, "Baseline", 0, 0},
38 { M_SOF1, "Extended sequential", 0, 0},
39 { M_SOF2, "Progressive", 0, 0},
40 { M_SOF3, "Lossless", 0, 0},
41 { M_SOF5, "Differential sequential", 0, 0},
42 { M_SOF6, "Differential progressive", 0, 0},
43 { M_SOF7, "Differential lossless", 0, 0},
44 { M_SOF9, "Extended sequential, arithmetic coding", 0, 0},
45 { M_SOF10, "Progressive, arithmetic coding", 0, 0},
46 { M_SOF11, "Lossless, arithmetic coding", 0, 0},
47 { M_SOF13, "Differential sequential, arithmetic coding", 0, 0},
48 { M_SOF14, "Differential progressive, arithmetic coding", 0, 0},
49 { M_SOF15, "Differential lossless, arithmetic coding", 0, 0},
50};
51
52#define PROCESS_TABLE_SIZE (sizeof(ProcessTable) / sizeof(TagTable_t))
53
54// 1 - "The 0th row is at the visual top of the image, and the 0th column is the visual left-hand side."
55// 2 - "The 0th row is at the visual top of the image, and the 0th column is the visual right-hand side."
56// 3 - "The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side."
57// 4 - "The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side."
58
59// 5 - "The 0th row is the visual left-hand side of of the image, and the 0th column is the visual top."
60// 6 - "The 0th row is the visual right-hand side of of the image, and the 0th column is the visual top."
61// 7 - "The 0th row is the visual right-hand side of of the image, and the 0th column is the visual bottom."
62// 8 - "The 0th row is the visual left-hand side of of the image, and the 0th column is the visual bottom."
63
64// Note: The descriptions here are the same as the name of the command line
65// option to pass to jpegtran to right the image
66
67static const char * OrientTab[9] = {
68 "Undefined",
69 "Normal", // 1
70 "flip horizontal", // left right reversed mirror
71 "rotate 180", // 3
72 "flip vertical", // upside down mirror
73 "transpose", // Flipped about top-left <--> bottom-right axis.
74 "rotate 90", // rotate 90 cw to right it.
75 "transverse", // flipped about top-right <--> bottom-left axis
76 "rotate 270", // rotate 270 to right it.
77};
78
79const int BytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8};
80
81//--------------------------------------------------------------------------
82// Describes tag values
83
Chih-Chung Changd6a02c32009-03-24 20:34:28 -070084#define TAG_INTEROP_INDEX 0x0001
85#define TAG_INTEROP_VERSION 0x0002
86#define TAG_IMAGE_WIDTH 0x0100
87#define TAG_IMAGE_LENGTH 0x0101
88#define TAG_BITS_PER_SAMPLE 0x0102
89#define TAG_COMPRESSION 0x0103
90#define TAG_PHOTOMETRIC_INTERP 0x0106
91#define TAG_FILL_ORDER 0x010A
92#define TAG_DOCUMENT_NAME 0x010D
93#define TAG_IMAGE_DESCRIPTION 0x010E
94#define TAG_MAKE 0x010F
95#define TAG_MODEL 0x0110
96#define TAG_SRIP_OFFSET 0x0111
97#define TAG_ORIENTATION 0x0112
98#define TAG_SAMPLES_PER_PIXEL 0x0115
99#define TAG_ROWS_PER_STRIP 0x0116
100#define TAG_STRIP_BYTE_COUNTS 0x0117
101#define TAG_X_RESOLUTION 0x011A
102#define TAG_Y_RESOLUTION 0x011B
103#define TAG_PLANAR_CONFIGURATION 0x011C
104#define TAG_RESOLUTION_UNIT 0x0128
105#define TAG_TRANSFER_FUNCTION 0x012D
106#define TAG_SOFTWARE 0x0131
107#define TAG_DATETIME 0x0132
108#define TAG_ARTIST 0x013B
109#define TAG_WHITE_POINT 0x013E
110#define TAG_PRIMARY_CHROMATICITIES 0x013F
111#define TAG_TRANSFER_RANGE 0x0156
112#define TAG_JPEG_PROC 0x0200
113#define TAG_THUMBNAIL_OFFSET 0x0201
114#define TAG_THUMBNAIL_LENGTH 0x0202
115#define TAG_Y_CB_CR_COEFFICIENTS 0x0211
116#define TAG_Y_CB_CR_SUB_SAMPLING 0x0212
117#define TAG_Y_CB_CR_POSITIONING 0x0213
118#define TAG_REFERENCE_BLACK_WHITE 0x0214
119#define TAG_RELATED_IMAGE_WIDTH 0x1001
120#define TAG_RELATED_IMAGE_LENGTH 0x1002
121#define TAG_CFA_REPEAT_PATTERN_DIM 0x828D
122#define TAG_CFA_PATTERN1 0x828E
123#define TAG_BATTERY_LEVEL 0x828F
124#define TAG_COPYRIGHT 0x8298
125#define TAG_EXPOSURETIME 0x829A
126#define TAG_FNUMBER 0x829D
127#define TAG_IPTC_NAA 0x83BB
128#define TAG_EXIF_OFFSET 0x8769
129#define TAG_INTER_COLOR_PROFILE 0x8773
130#define TAG_EXPOSURE_PROGRAM 0x8822
131#define TAG_SPECTRAL_SENSITIVITY 0x8824
132#define TAG_GPSINFO 0x8825
133#define TAG_ISO_EQUIVALENT 0x8827
134#define TAG_OECF 0x8828
135#define TAG_EXIF_VERSION 0x9000
136#define TAG_DATETIME_ORIGINAL 0x9003
137#define TAG_DATETIME_DIGITIZED 0x9004
138#define TAG_COMPONENTS_CONFIG 0x9101
139#define TAG_CPRS_BITS_PER_PIXEL 0x9102
140#define TAG_SHUTTERSPEED 0x9201
141#define TAG_APERTURE 0x9202
142#define TAG_BRIGHTNESS_VALUE 0x9203
143#define TAG_EXPOSURE_BIAS 0x9204
144#define TAG_MAXAPERTURE 0x9205
145#define TAG_SUBJECT_DISTANCE 0x9206
146#define TAG_METERING_MODE 0x9207
147#define TAG_LIGHT_SOURCE 0x9208
148#define TAG_FLASH 0x9209
149#define TAG_FOCALLENGTH 0x920A
150#define TAG_MAKER_NOTE 0x927C
151#define TAG_USERCOMMENT 0x9286
152#define TAG_SUBSEC_TIME 0x9290
153#define TAG_SUBSEC_TIME_ORIG 0x9291
154#define TAG_SUBSEC_TIME_DIG 0x9292
155
156#define TAG_WINXP_TITLE 0x9c9b // Windows XP - not part of exif standard.
157#define TAG_WINXP_COMMENT 0x9c9c // Windows XP - not part of exif standard.
158#define TAG_WINXP_AUTHOR 0x9c9d // Windows XP - not part of exif standard.
159#define TAG_WINXP_KEYWORDS 0x9c9e // Windows XP - not part of exif standard.
160#define TAG_WINXP_SUBJECT 0x9c9f // Windows XP - not part of exif standard.
161
162#define TAG_FLASH_PIX_VERSION 0xA000
163#define TAG_COLOR_SPACE 0xA001
164#define TAG_EXIF_IMAGEWIDTH 0xA002
165#define TAG_EXIF_IMAGELENGTH 0xA003
166#define TAG_RELATED_AUDIO_FILE 0xA004
167#define TAG_INTEROP_OFFSET 0xA005
168#define TAG_FLASH_ENERGY 0xA20B
169#define TAG_SPATIAL_FREQ_RESP 0xA20C
170#define TAG_FOCAL_PLANE_XRES 0xA20E
171#define TAG_FOCAL_PLANE_YRES 0xA20F
172#define TAG_FOCAL_PLANE_UNITS 0xA210
173#define TAG_SUBJECT_LOCATION 0xA214
174#define TAG_EXPOSURE_INDEX 0xA215
175#define TAG_SENSING_METHOD 0xA217
176#define TAG_FILE_SOURCE 0xA300
177#define TAG_SCENE_TYPE 0xA301
178#define TAG_CFA_PATTERN 0xA302
179#define TAG_CUSTOM_RENDERED 0xA401
180#define TAG_EXPOSURE_MODE 0xA402
181#define TAG_WHITEBALANCE 0xA403
182#define TAG_DIGITALZOOMRATIO 0xA404
183#define TAG_FOCALLENGTH_35MM 0xA405
184#define TAG_SCENE_CAPTURE_TYPE 0xA406
185#define TAG_GAIN_CONTROL 0xA407
186#define TAG_CONTRAST 0xA408
187#define TAG_SATURATION 0xA409
188#define TAG_SHARPNESS 0xA40A
189#define TAG_DISTANCE_RANGE 0xA40C
The Android Open Source Project34a25642009-03-03 19:30:03 -0800190
191// TODO: replace the ", 0" values in this table with the correct format, e.g. ", FMT_USHORT"
192static const TagTable_t TagTable[] = {
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700193 { TAG_INTEROP_INDEX, "InteropIndex", 0, 0},
194 { TAG_INTEROP_VERSION, "InteropVersion", 0, 0},
195 { TAG_IMAGE_WIDTH, "ImageWidth", FMT_USHORT, 1},
196 { TAG_IMAGE_LENGTH, "ImageLength", FMT_USHORT, 1},
197 { TAG_BITS_PER_SAMPLE, "BitsPerSample", FMT_USHORT, 3},
198 { TAG_COMPRESSION, "Compression", FMT_USHORT, 1},
199 { TAG_PHOTOMETRIC_INTERP, "PhotometricInterpretation", FMT_USHORT, 1},
200 { TAG_FILL_ORDER, "FillOrder", 0, 0},
201 { TAG_DOCUMENT_NAME, "DocumentName", 0, 0},
202 { TAG_IMAGE_DESCRIPTION, "ImageDescription", 0, 0 },
203 { TAG_MAKE, "Make", FMT_STRING, -1},
204 { TAG_MODEL, "Model", FMT_STRING, -1},
205 { TAG_SRIP_OFFSET, "StripOffsets", FMT_USHORT, 1},
206 { TAG_ORIENTATION, "Orientation", FMT_USHORT, 1},
207 { TAG_SAMPLES_PER_PIXEL, "SamplesPerPixel", FMT_USHORT, 3},
208 { TAG_ROWS_PER_STRIP, "RowsPerStrip", FMT_USHORT, 1},
209 { TAG_STRIP_BYTE_COUNTS, "StripByteCounts", FMT_USHORT, 1},
210 { TAG_X_RESOLUTION, "XResolution", FMT_URATIONAL, 1},
211 { TAG_Y_RESOLUTION, "YResolution", FMT_URATIONAL, 1},
212 { TAG_PLANAR_CONFIGURATION, "PlanarConfiguration", FMT_USHORT, 1},
213 { TAG_RESOLUTION_UNIT, "ResolutionUnit", FMT_USHORT, 1},
214 { TAG_TRANSFER_FUNCTION, "TransferFunction", FMT_USHORT, 768},
215 { TAG_SOFTWARE, "Software", FMT_STRING, -1},
216 { TAG_DATETIME, "DateTime", FMT_STRING, 20},
217 { TAG_ARTIST, "Artist", FMT_STRING, -1},
218 { TAG_WHITE_POINT, "WhitePoint", FMT_SRATIONAL, 2},
219 { TAG_PRIMARY_CHROMATICITIES, "PrimaryChromaticities", FMT_SRATIONAL, 6},
220 { TAG_TRANSFER_RANGE, "TransferRange", 0, 0},
221 { TAG_JPEG_PROC, "JPEGProc", 0, 0},
222 { TAG_THUMBNAIL_OFFSET, "ThumbnailOffset", 0, 0},
223 { TAG_THUMBNAIL_LENGTH, "ThumbnailLength", 0, 0},
224 { TAG_Y_CB_CR_COEFFICIENTS, "YCbCrCoefficients", FMT_SRATIONAL, 3},
225 { TAG_Y_CB_CR_SUB_SAMPLING, "YCbCrSubSampling", FMT_USHORT, 2},
226 { TAG_Y_CB_CR_POSITIONING, "YCbCrPositioning", FMT_USHORT, 1},
227 { TAG_REFERENCE_BLACK_WHITE, "ReferenceBlackWhite", FMT_SRATIONAL, 6},
228 { TAG_RELATED_IMAGE_WIDTH, "RelatedImageWidth", 0, 0},
229 { TAG_RELATED_IMAGE_LENGTH, "RelatedImageLength", 0, 0},
230 { TAG_CFA_REPEAT_PATTERN_DIM, "CFARepeatPatternDim", 0, 0},
231 { TAG_CFA_PATTERN1, "CFAPattern", 0, 0},
232 { TAG_BATTERY_LEVEL, "BatteryLevel", 0, 0},
233 { TAG_COPYRIGHT, "Copyright", FMT_STRING, -1},
Tyler Luu75407802011-10-19 01:36:15 -0500234 { TAG_EXPOSURETIME, "ExposureTime", FMT_SRATIONAL, 1},
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700235 { TAG_FNUMBER, "FNumber", FMT_SRATIONAL, 1},
236 { TAG_IPTC_NAA, "IPTC/NAA", 0, 0},
237 { TAG_EXIF_OFFSET, "ExifOffset", 0, 0},
238 { TAG_INTER_COLOR_PROFILE, "InterColorProfile", 0, 0},
239 { TAG_EXPOSURE_PROGRAM, "ExposureProgram", FMT_SSHORT, 1},
240 { TAG_SPECTRAL_SENSITIVITY, "SpectralSensitivity", FMT_STRING, -1},
241 { TAG_GPSINFO, "GPS Dir offset", 0, 0},
242 { TAG_ISO_EQUIVALENT, "ISOSpeedRatings", FMT_SSHORT, -1},
243 { TAG_OECF, "OECF", 0, 0},
244 { TAG_EXIF_VERSION, "ExifVersion", FMT_BYTE, 4},
245 { TAG_DATETIME_ORIGINAL, "DateTimeOriginal", FMT_STRING, 20},
246 { TAG_DATETIME_DIGITIZED, "DateTimeDigitized", FMT_STRING, 20},
247 { TAG_COMPONENTS_CONFIG, "ComponentsConfiguration", FMT_BYTE, 4},
248 { TAG_CPRS_BITS_PER_PIXEL, "CompressedBitsPerPixel", FMT_SRATIONAL, 1},
249 { TAG_SHUTTERSPEED, "ShutterSpeedValue", FMT_SRATIONAL, 1},
250 { TAG_APERTURE, "ApertureValue", FMT_URATIONAL, 1},
251 { TAG_BRIGHTNESS_VALUE, "BrightnessValue", FMT_SRATIONAL, 1},
252 { TAG_EXPOSURE_BIAS, "ExposureBiasValue", FMT_SRATIONAL, 1},
253 { TAG_MAXAPERTURE, "MaxApertureValue", FMT_URATIONAL, 1},
254 { TAG_SUBJECT_DISTANCE, "SubjectDistance", FMT_URATIONAL, 1},
255 { TAG_METERING_MODE, "MeteringMode", FMT_USHORT, 1},
256 { TAG_LIGHT_SOURCE, "LightSource", FMT_USHORT, 1},
257 { TAG_FLASH, "Flash", FMT_USHORT, 1},
258 { TAG_FOCALLENGTH, "FocalLength", FMT_URATIONAL, 1},
259 { TAG_MAKER_NOTE, "MakerNote", FMT_STRING, -1},
260 { TAG_USERCOMMENT, "UserComment", FMT_STRING, -1},
261 { TAG_SUBSEC_TIME, "SubSecTime", FMT_STRING, -1},
262 { TAG_SUBSEC_TIME_ORIG, "SubSecTimeOriginal", FMT_STRING, -1},
263 { TAG_SUBSEC_TIME_DIG, "SubSecTimeDigitized", FMT_STRING, -1},
264 { TAG_WINXP_TITLE, "Windows-XP Title", 0, 0},
265 { TAG_WINXP_COMMENT, "Windows-XP comment", 0, 0},
266 { TAG_WINXP_AUTHOR, "Windows-XP author", 0, 0},
267 { TAG_WINXP_KEYWORDS, "Windows-XP keywords", 0, 0},
268 { TAG_WINXP_SUBJECT, "Windows-XP subject", 0, 0},
269 { TAG_FLASH_PIX_VERSION, "FlashPixVersion", FMT_BYTE, 4},
270 { TAG_COLOR_SPACE, "ColorSpace", FMT_USHORT, 1},
271 { TAG_EXIF_IMAGEWIDTH, "ExifImageWidth", 0, 0},
272 { TAG_EXIF_IMAGELENGTH, "ExifImageLength", 0, 0},
273 { TAG_RELATED_AUDIO_FILE, "RelatedAudioFile", 0, 0},
274 { TAG_INTEROP_OFFSET, "InteroperabilityOffset", 0, 0},
275 { TAG_FLASH_ENERGY, "FlashEnergy", FMT_URATIONAL, 1},
276 { TAG_SPATIAL_FREQ_RESP, "SpatialFrequencyResponse", FMT_STRING, -1},
277 { TAG_FOCAL_PLANE_XRES, "FocalPlaneXResolution", FMT_URATIONAL, 1},
278 { TAG_FOCAL_PLANE_YRES, "FocalPlaneYResolution", FMT_URATIONAL, 1},
279 { TAG_FOCAL_PLANE_UNITS, "FocalPlaneResolutionUnit", FMT_USHORT, 1},
280 { TAG_SUBJECT_LOCATION, "SubjectLocation", FMT_USHORT, 2},
281 { TAG_EXPOSURE_INDEX, "ExposureIndex", FMT_URATIONAL, 1},
282 { TAG_SENSING_METHOD, "SensingMethod", FMT_USHORT, 1},
283 { TAG_FILE_SOURCE, "FileSource", 0, 1},
284 { TAG_SCENE_TYPE, "SceneType", 0, 1},
285 { TAG_CFA_PATTERN, "CFA Pattern", 0, -1},
286 { TAG_CUSTOM_RENDERED, "CustomRendered", FMT_USHORT, 1},
287 { TAG_EXPOSURE_MODE, "ExposureMode", FMT_USHORT, 1},
288 { TAG_WHITEBALANCE, "WhiteBalance", FMT_USHORT, 1},
289 { TAG_DIGITALZOOMRATIO, "DigitalZoomRatio", FMT_URATIONAL, 1},
290 { TAG_FOCALLENGTH_35MM, "FocalLengthIn35mmFilm", FMT_USHORT, 1},
291 { TAG_SCENE_CAPTURE_TYPE, "SceneCaptureType", FMT_USHORT, 1},
292 { TAG_GAIN_CONTROL, "GainControl", FMT_URATIONAL, 1},
293 { TAG_CONTRAST, "Contrast", FMT_USHORT, 1},
294 { TAG_SATURATION, "Saturation", FMT_USHORT, 1},
295 { TAG_SHARPNESS, "Sharpness", FMT_USHORT, 1},
296 { TAG_DISTANCE_RANGE, "SubjectDistanceRange", FMT_USHORT, 1},
The Android Open Source Project34a25642009-03-03 19:30:03 -0800297} ;
298
299#define TAG_TABLE_SIZE (sizeof(TagTable) / sizeof(TagTable_t))
300
301int TagNameToValue(const char* tagName)
302{
303 unsigned int i;
304 for (i = 0; i < TAG_TABLE_SIZE; i++) {
305 if (strcmp(TagTable[i].Desc, tagName) == 0) {
306 printf("found tag %s val %d", TagTable[i].Desc, TagTable[i].Tag);
307 return TagTable[i].Tag;
308 }
309 }
310 printf("tag %s NOT FOUND", tagName);
311 return -1;
312}
313
Angus Kong482486a2012-01-19 15:56:10 +0800314int IsDateTimeTag(unsigned short tag)
315{
316 return ((tag == TAG_DATETIME)? TRUE: FALSE);
317}
318
The Android Open Source Project34a25642009-03-03 19:30:03 -0800319//--------------------------------------------------------------------------
320// Convert a 16 bit unsigned value to file's native byte order
321//--------------------------------------------------------------------------
322static void Put16u(void * Short, unsigned short PutValue)
323{
324 if (MotorolaOrder){
325 ((uchar *)Short)[0] = (uchar)(PutValue>>8);
326 ((uchar *)Short)[1] = (uchar)PutValue;
327 }else{
328 ((uchar *)Short)[0] = (uchar)PutValue;
329 ((uchar *)Short)[1] = (uchar)(PutValue>>8);
330 }
331}
332
333//--------------------------------------------------------------------------
334// Convert a 16 bit unsigned value from file's native byte order
335//--------------------------------------------------------------------------
336int Get16u(void * Short)
337{
338 if (MotorolaOrder){
339 return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1];
340 }else{
341 return (((uchar *)Short)[1] << 8) | ((uchar *)Short)[0];
342 }
343}
344
345//--------------------------------------------------------------------------
346// Convert a 32 bit signed value from file's native byte order
347//--------------------------------------------------------------------------
348int Get32s(void * Long)
349{
350 if (MotorolaOrder){
351 return ((( char *)Long)[0] << 24) | (((uchar *)Long)[1] << 16)
352 | (((uchar *)Long)[2] << 8 ) | (((uchar *)Long)[3] << 0 );
353 }else{
354 return ((( char *)Long)[3] << 24) | (((uchar *)Long)[2] << 16)
355 | (((uchar *)Long)[1] << 8 ) | (((uchar *)Long)[0] << 0 );
356 }
357}
358
359//--------------------------------------------------------------------------
360// Convert a 32 bit unsigned value to file's native byte order
361//--------------------------------------------------------------------------
362void Put32u(void * Value, unsigned PutValue)
363{
364 if (MotorolaOrder){
365 ((uchar *)Value)[0] = (uchar)(PutValue>>24);
366 ((uchar *)Value)[1] = (uchar)(PutValue>>16);
367 ((uchar *)Value)[2] = (uchar)(PutValue>>8);
368 ((uchar *)Value)[3] = (uchar)PutValue;
369 }else{
370 ((uchar *)Value)[0] = (uchar)PutValue;
371 ((uchar *)Value)[1] = (uchar)(PutValue>>8);
372 ((uchar *)Value)[2] = (uchar)(PutValue>>16);
373 ((uchar *)Value)[3] = (uchar)(PutValue>>24);
374 }
375}
376
377//--------------------------------------------------------------------------
378// Convert a 32 bit unsigned value from file's native byte order
379//--------------------------------------------------------------------------
380unsigned Get32u(void * Long)
381{
382 return (unsigned)Get32s(Long) & 0xffffffff;
383}
384
385//--------------------------------------------------------------------------
386// Display a number as one of its many formats
387//--------------------------------------------------------------------------
388void PrintFormatNumber(void * ValuePtr, int Format, int ByteCount)
389{
390 int s,n;
391
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700392 for(n=0;n<16;n++){
The Android Open Source Project34a25642009-03-03 19:30:03 -0800393 switch(Format){
394 case FMT_SBYTE:
395 case FMT_BYTE: printf("%02x",*(uchar *)ValuePtr); s=1; break;
396 case FMT_USHORT: printf("%d",Get16u(ValuePtr)); s=2; break;
397 case FMT_ULONG:
398 case FMT_SLONG: printf("%d",Get32s(ValuePtr)); s=4; break;
399 case FMT_SSHORT: printf("%hd",(signed short)Get16u(ValuePtr)); s=2; break;
400 case FMT_URATIONAL:
401 case FMT_SRATIONAL:
402 printf("%d/%d",Get32s(ValuePtr), Get32s(4+(char *)ValuePtr));
403 s = 8;
404 break;
405
406 case FMT_SINGLE: printf("%f",(double)*(float *)ValuePtr); s=8; break;
407 case FMT_DOUBLE: printf("%f",*(double *)ValuePtr); s=8; break;
408 default:
409 printf("Unknown format %d:", Format);
410 return;
411 }
412 ByteCount -= s;
413 if (ByteCount <= 0) break;
414 printf(", ");
415 ValuePtr = (void *)((char *)ValuePtr + s);
416
417 }
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700418 if (n >= 16) printf("...");
The Android Open Source Project34a25642009-03-03 19:30:03 -0800419}
420
421
422//--------------------------------------------------------------------------
423// Evaluate number, be it int, rational, or float from directory.
424//--------------------------------------------------------------------------
425double ConvertAnyFormat(void * ValuePtr, int Format)
426{
427 double Value;
428 Value = 0;
429
430 switch(Format){
431 case FMT_SBYTE: Value = *(signed char *)ValuePtr; break;
432 case FMT_BYTE: Value = *(uchar *)ValuePtr; break;
433
434 case FMT_USHORT: Value = Get16u(ValuePtr); break;
435 case FMT_ULONG: Value = Get32u(ValuePtr); break;
436
437 case FMT_URATIONAL:
438 case FMT_SRATIONAL:
439 {
440 int Num,Den;
441 Num = Get32s(ValuePtr);
442 Den = Get32s(4+(char *)ValuePtr);
443 if (Den == 0){
444 Value = 0;
445 }else{
446 Value = (double)Num/Den;
447 }
448 break;
449 }
450
451 case FMT_SSHORT: Value = (signed short)Get16u(ValuePtr); break;
452 case FMT_SLONG: Value = Get32s(ValuePtr); break;
453
454 // Not sure if this is correct (never seen float used in Exif format)
455 case FMT_SINGLE: Value = (double)*(float *)ValuePtr; break;
456 case FMT_DOUBLE: Value = *(double *)ValuePtr; break;
457
458 default:
459 ErrNonfatal("Illegal format code %d",Format,0);
460 }
461 return Value;
462}
463
464//--------------------------------------------------------------------------
465// Process one of the nested EXIF directories.
466//--------------------------------------------------------------------------
467static void ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase,
468 unsigned ExifLength, int NestingLevel)
469{
470 int de;
471 int a;
472 int NumDirEntries;
473 unsigned ThumbnailOffset = 0;
474 unsigned ThumbnailSize = 0;
475 char IndentString[25];
476
477 printf("ProcessExifDir");
478 if (NestingLevel > 4){
479 ErrNonfatal("Maximum directory nesting exceeded (corrupt exif header)", 0,0);
480 return;
481 }
482
483 memset(IndentString, ' ', 25);
484 IndentString[NestingLevel * 4] = '\0';
485
486
487 NumDirEntries = Get16u(DirStart);
488 #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry))
489
490 {
491 unsigned char * DirEnd;
492 DirEnd = DIR_ENTRY_ADDR(DirStart, NumDirEntries);
493 if (DirEnd+4 > (OffsetBase+ExifLength)){
494 if (DirEnd+2 == OffsetBase+ExifLength || DirEnd == OffsetBase+ExifLength){
495 // Version 1.3 of jhead would truncate a bit too much.
496 // This also caught later on as well.
497 }else{
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700498 ErrNonfatal("Illegally sized exif subdirectory (%d entries)",NumDirEntries,0);
The Android Open Source Project34a25642009-03-03 19:30:03 -0800499 return;
500 }
501 }
502 if (DumpExifMap){
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700503 printf("Map: %05d-%05d: Directory\n",(int)(DirStart-OffsetBase), (int)(DirEnd+4-OffsetBase));
The Android Open Source Project34a25642009-03-03 19:30:03 -0800504 }
505
506
507 }
508
509 if (ShowTags){
510 printf("(dir has %d entries)\n",NumDirEntries);
511 }
512
513 for (de=0;de<NumDirEntries;de++){
514 int Tag, Format, Components;
515 unsigned char * ValuePtr;
516 int ByteCount;
517 unsigned char * DirEntry;
518 DirEntry = DIR_ENTRY_ADDR(DirStart, de);
519
520 Tag = Get16u(DirEntry);
521 Format = Get16u(DirEntry+2);
522 Components = Get32u(DirEntry+4);
523
524 if ((Format-1) >= NUM_FORMATS) {
525 // (-1) catches illegal zero case as unsigned underflows to positive large.
526 ErrNonfatal("Illegal number format %d for tag %04x", Format, Tag);
527 continue;
528 }
529
530 if ((unsigned)Components > 0x10000){
531 ErrNonfatal("Illegal number of components %d for tag %04x", Components, Tag);
532 continue;
533 }
534
535 ByteCount = Components * BytesPerFormat[Format];
536
537 if (ByteCount > 4){
538 unsigned OffsetVal;
539 OffsetVal = Get32u(DirEntry+8);
540 // If its bigger than 4 bytes, the dir entry contains an offset.
541 if (OffsetVal+ByteCount > ExifLength){
542 // Bogus pointer offset and / or bytecount value
543 ErrNonfatal("Illegal value pointer for tag %04x", Tag,0);
544 continue;
545 }
546 ValuePtr = OffsetBase+OffsetVal;
547
548 if (OffsetVal > ImageInfo.LargestExifOffset){
549 ImageInfo.LargestExifOffset = OffsetVal;
550 }
551
552 if (DumpExifMap){
553 printf("Map: %05d-%05d: Data for tag %04x\n",OffsetVal, OffsetVal+ByteCount, Tag);
554 }
555 }else{
556 // 4 bytes or less and value is in the dir entry itself
557 ValuePtr = DirEntry+8;
558 }
559
560 if (Tag == TAG_MAKER_NOTE){
561 if (ShowTags){
562 printf("%s Maker note: ",IndentString);
563 }
564 ProcessMakerNote(ValuePtr, ByteCount, OffsetBase, ExifLength);
565 continue;
566 }
567
568 if (ShowTags){
569 // Show tag name
570 for (a=0;;a++){
571 if (a >= (int)TAG_TABLE_SIZE){
Nick Kralevich70059dd2010-05-14 11:48:59 -0700572 printf("%s", IndentString);
The Android Open Source Project34a25642009-03-03 19:30:03 -0800573 printf(" Unknown Tag %04x Value = ", Tag);
574 break;
575 }
576 if (TagTable[a].Tag == Tag){
Nick Kralevich70059dd2010-05-14 11:48:59 -0700577 printf("%s", IndentString);
The Android Open Source Project34a25642009-03-03 19:30:03 -0800578 printf(" %s = ",TagTable[a].Desc);
579 break;
580 }
581 }
582
583 // Show tag value.
584 switch(Format){
585 case FMT_BYTE:
586 if(ByteCount>1){
587 printf("%.*ls\n", ByteCount/2, (wchar_t *)ValuePtr);
588 }else{
589 PrintFormatNumber(ValuePtr, Format, ByteCount);
590 printf("\n");
591 }
592 break;
593
594 case FMT_UNDEFINED:
595 // Undefined is typically an ascii string.
596
597 case FMT_STRING:
598 // String arrays printed without function call (different from int arrays)
599 {
600 printf("\"%s\"", ValuePtr);
601// int NoPrint = 0;
602// printf("\"");
603// for (a=0;a<ByteCount;a++){
604// if (ValuePtr[a] >= 32){
605// putchar(ValuePtr[a]);
606// NoPrint = 0;
607// }else{
608// // Avoiding indicating too many unprintable characters of proprietary
609// // bits of binary information this program may not know how to parse.
610// if (!NoPrint && a != ByteCount-1){
611// putchar('?');
612// NoPrint = 1;
613// }
614// }
615// }
616// printf("\"\n");
617 }
618 break;
619
620 default:
621 // Handle arrays of numbers later (will there ever be?)
622 PrintFormatNumber(ValuePtr, Format, ByteCount);
623 printf("\n");
624 }
625 }
626
627 // Extract useful components of tag
628 switch(Tag){
629
630 case TAG_MAKE:
631 strncpy(ImageInfo.CameraMake, (char *)ValuePtr, ByteCount < 31 ? ByteCount : 31);
632 break;
633
634 case TAG_MODEL:
635 strncpy(ImageInfo.CameraModel, (char *)ValuePtr, ByteCount < 39 ? ByteCount : 39);
636 break;
637
638 case TAG_DATETIME_ORIGINAL:
639 // If we get a DATETIME_ORIGINAL, we use that one.
640 strncpy(ImageInfo.DateTime, (char *)ValuePtr, 19);
641 // Fallthru...
642
643 case TAG_DATETIME_DIGITIZED:
644 case TAG_DATETIME:
645 if (!isdigit(ImageInfo.DateTime[0])){
646 // If we don't already have a DATETIME_ORIGINAL, use whatever
647 // time fields we may have.
648 strncpy(ImageInfo.DateTime, (char *)ValuePtr, 19);
649 }
650
651 if (ImageInfo.numDateTimeTags >= MAX_DATE_COPIES){
652 ErrNonfatal("More than %d date fields! This is nuts", MAX_DATE_COPIES, 0);
653 break;
654 }
655 ImageInfo.DateTimeOffsets[ImageInfo.numDateTimeTags++] =
656 (char *)ValuePtr - (char *)OffsetBase;
657 break;
658
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700659 case TAG_WINXP_COMMENT:
660 if (ImageInfo.Comments[0]){ // We already have a jpeg comment.
661 // Already have a comment (probably windows comment), skip this one.
662 if (ShowTags) printf("Windows XP commend and other comment in header\n");
663 break; // Already have a windows comment, skip this one.
664 }
665
666 if (ByteCount > 1){
667 if (ByteCount > MAX_COMMENT_SIZE) ByteCount = MAX_COMMENT_SIZE;
668 memcpy(ImageInfo.Comments, ValuePtr, ByteCount);
669 ImageInfo.CommentWidchars = ByteCount/2;
670 }
671 break;
The Android Open Source Project34a25642009-03-03 19:30:03 -0800672
673 case TAG_USERCOMMENT:
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700674 if (ImageInfo.Comments[0]){ // We already have a jpeg comment.
675 // Already have a comment (probably windows comment), skip this one.
676 if (ShowTags) printf("Multiple comments in exif header\n");
677 break; // Already have a windows comment, skip this one.
678 }
679
680 // Comment is often padded with trailing spaces. Remove these first.
The Android Open Source Project34a25642009-03-03 19:30:03 -0800681 for (a=ByteCount;;){
682 a--;
683 if ((ValuePtr)[a] == ' '){
684 (ValuePtr)[a] = '\0';
685 }else{
686 break;
687 }
688 if (a == 0) break;
689 }
690
691 // Copy the comment
692 if (memcmp(ValuePtr, "ASCII",5) == 0){
693 for (a=5;a<10;a++){
694 int c;
695 c = (ValuePtr)[a];
696 if (c != '\0' && c != ' '){
697 strncpy(ImageInfo.Comments, (char *)ValuePtr+a, 199);
698 break;
699 }
700 }
The Android Open Source Project34a25642009-03-03 19:30:03 -0800701 }else{
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700702 strncpy(ImageInfo.Comments, (char *)ValuePtr, MAX_COMMENT_SIZE-1);
The Android Open Source Project34a25642009-03-03 19:30:03 -0800703 }
704 break;
705
706 case TAG_FNUMBER:
707 // Simplest way of expressing aperture, so I trust it the most.
708 // (overwrite previously computd value if there is one)
709 ImageInfo.ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format);
710 break;
711
712 case TAG_APERTURE:
713 case TAG_MAXAPERTURE:
714 // More relevant info always comes earlier, so only use this field if we don't
715 // have appropriate aperture information yet.
716 if (ImageInfo.ApertureFNumber == 0){
717 ImageInfo.ApertureFNumber
718 = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2)*0.5);
719 }
720 break;
721
722 case TAG_FOCALLENGTH:
723 // Nice digital cameras actually save the focal length as a function
724 // of how farthey are zoomed in.
Wu-cheng Li574d52d2010-01-31 22:34:33 +0800725 ImageInfo.FocalLength.num = Get32u(ValuePtr);
726 ImageInfo.FocalLength.denom = Get32u(4+(char *)ValuePtr);
The Android Open Source Project34a25642009-03-03 19:30:03 -0800727 break;
728
729 case TAG_SUBJECT_DISTANCE:
730 // Inidcates the distacne the autofocus camera is focused to.
731 // Tends to be less accurate as distance increases.
732 ImageInfo.Distance = (float)ConvertAnyFormat(ValuePtr, Format);
733 break;
734
735 case TAG_EXPOSURETIME:
736 // Simplest way of expressing exposure time, so I trust it most.
737 // (overwrite previously computd value if there is one)
738 ImageInfo.ExposureTime = (float)ConvertAnyFormat(ValuePtr, Format);
739 break;
740
741 case TAG_SHUTTERSPEED:
742 // More complicated way of expressing exposure time, so only use
743 // this value if we don't already have it from somewhere else.
744 if (ImageInfo.ExposureTime == 0){
745 ImageInfo.ExposureTime
746 = (float)(1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2)));
747 }
748 break;
749
750
751 case TAG_FLASH:
752 ImageInfo.FlashUsed=(int)ConvertAnyFormat(ValuePtr, Format);
753 break;
754
755 case TAG_ORIENTATION:
756 if (NumOrientations >= 2){
757 // Can have another orientation tag for the thumbnail, but if there's
758 // a third one, things are stringae.
759 ErrNonfatal("More than two orientation tags!",0,0);
760 break;
761 }
762 OrientationPtr[NumOrientations] = ValuePtr;
763 OrientationNumFormat[NumOrientations] = Format;
764 if (NumOrientations == 0){
765 ImageInfo.Orientation = (int)ConvertAnyFormat(ValuePtr, Format);
766 }
767 if (ImageInfo.Orientation < 0 || ImageInfo.Orientation > 8){
768 ErrNonfatal("Undefined rotation value %d", ImageInfo.Orientation, 0);
769 ImageInfo.Orientation = 0;
770 }
771 NumOrientations += 1;
772 break;
773
774 case TAG_EXIF_IMAGELENGTH:
775 case TAG_EXIF_IMAGEWIDTH:
776 // Use largest of height and width to deal with images that have been
777 // rotated to portrait format.
778 a = (int)ConvertAnyFormat(ValuePtr, Format);
779 if (ExifImageWidth < a) ExifImageWidth = a;
780 break;
781
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700782 case TAG_FOCAL_PLANE_XRES:
The Android Open Source Project34a25642009-03-03 19:30:03 -0800783 FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format);
784 break;
785
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700786 case TAG_FOCAL_PLANE_UNITS:
The Android Open Source Project34a25642009-03-03 19:30:03 -0800787 switch((int)ConvertAnyFormat(ValuePtr, Format)){
788 case 1: FocalplaneUnits = 25.4; break; // inch
789 case 2:
790 // According to the information I was using, 2 means meters.
791 // But looking at the Cannon powershot's files, inches is the only
792 // sensible value.
793 FocalplaneUnits = 25.4;
794 break;
795
796 case 3: FocalplaneUnits = 10; break; // centimeter
797 case 4: FocalplaneUnits = 1; break; // millimeter
798 case 5: FocalplaneUnits = .001; break; // micrometer
799 }
800 break;
801
802 case TAG_EXPOSURE_BIAS:
803 ImageInfo.ExposureBias = (float)ConvertAnyFormat(ValuePtr, Format);
804 break;
805
806 case TAG_WHITEBALANCE:
807 ImageInfo.Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format);
808 break;
809
810 case TAG_LIGHT_SOURCE:
811 ImageInfo.LightSource = (int)ConvertAnyFormat(ValuePtr, Format);
812 break;
813
814 case TAG_METERING_MODE:
815 ImageInfo.MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format);
816 break;
817
818 case TAG_EXPOSURE_PROGRAM:
819 ImageInfo.ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format);
820 break;
821
822 case TAG_EXPOSURE_INDEX:
823 if (ImageInfo.ISOequivalent == 0){
824 // Exposure index and ISO equivalent are often used interchangeably,
825 // so we will do the same in jhead.
826 // http://photography.about.com/library/glossary/bldef_ei.htm
827 ImageInfo.ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
828 }
829 break;
830
831 case TAG_EXPOSURE_MODE:
832 ImageInfo.ExposureMode = (int)ConvertAnyFormat(ValuePtr, Format);
833 break;
834
835 case TAG_ISO_EQUIVALENT:
836 ImageInfo.ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
837 if ( ImageInfo.ISOequivalent < 50 ){
838 // Fixes strange encoding on some older digicams.
839 ImageInfo.ISOequivalent *= 200;
840 }
841 break;
842
843 case TAG_DIGITALZOOMRATIO:
844 ImageInfo.DigitalZoomRatio = (float)ConvertAnyFormat(ValuePtr, Format);
845 break;
846
847 case TAG_THUMBNAIL_OFFSET:
848 ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format);
849 DirWithThumbnailPtrs = DirStart;
850 break;
851
852 case TAG_THUMBNAIL_LENGTH:
853 ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format);
854 ImageInfo.ThumbnailSizeOffset = ValuePtr-OffsetBase;
855 break;
856
857 case TAG_EXIF_OFFSET:
858 if (ShowTags) printf("%s Exif Dir:",IndentString);
859
860 case TAG_INTEROP_OFFSET:
861 if (Tag == TAG_INTEROP_OFFSET && ShowTags) printf("%s Interop Dir:",IndentString);
862 {
863 unsigned char * SubdirStart;
864 SubdirStart = OffsetBase + Get32u(ValuePtr);
865 if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){
866 ErrNonfatal("Illegal exif or interop ofset directory link",0,0);
867 }else{
868 ProcessExifDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1);
869 }
870 continue;
871 }
872 break;
873
874 case TAG_GPSINFO:
875 if (ShowTags) printf("%s GPS info dir:",IndentString);
876 {
877 unsigned char * SubdirStart;
878 SubdirStart = OffsetBase + Get32u(ValuePtr);
879 if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){
880 ErrNonfatal("Illegal GPS directory link",0,0);
881 }else{
882 ProcessGpsInfo(SubdirStart, ByteCount, OffsetBase, ExifLength);
883 }
884 continue;
885 }
886 break;
887
888 case TAG_FOCALLENGTH_35MM:
889 // The focal length equivalent 35 mm is a 2.2 tag (defined as of April 2002)
890 // if its present, use it to compute equivalent focal length instead of
891 // computing it from sensor geometry and actual focal length.
892 ImageInfo.FocalLength35mmEquiv = (unsigned)ConvertAnyFormat(ValuePtr, Format);
893 break;
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700894
895 case TAG_DISTANCE_RANGE:
896 // Three possible standard values:
897 // 1 = macro, 2 = close, 3 = distant
898 ImageInfo.DistanceRange = (int)ConvertAnyFormat(ValuePtr, Format);
899 break;
The Android Open Source Project34a25642009-03-03 19:30:03 -0800900 }
901 }
902
903
904 {
905 // In addition to linking to subdirectories via exif tags,
906 // there's also a potential link to another directory at the end of each
907 // directory. this has got to be the result of a committee!
908 unsigned char * SubdirStart;
909 unsigned Offset;
910
911 if (DIR_ENTRY_ADDR(DirStart, NumDirEntries) + 4 <= OffsetBase+ExifLength){
912 printf("DirStart %d offset from dirstart %d", (int)DirStart, 2+12*NumDirEntries);
913 Offset = Get32u(DirStart+2+12*NumDirEntries);
914 if (Offset){
915 SubdirStart = OffsetBase + Offset;
916 if (SubdirStart > OffsetBase+ExifLength || SubdirStart < OffsetBase){
917 printf("SubdirStart %d OffsetBase %d ExifLength %d Offset %d",
918 (int)SubdirStart, (int)OffsetBase, ExifLength, Offset);
919 if (SubdirStart > OffsetBase && SubdirStart < OffsetBase+ExifLength+20){
920 // Jhead 1.3 or earlier would crop the whole directory!
921 // As Jhead produces this form of format incorrectness,
922 // I'll just let it pass silently
923 if (ShowTags) printf("Thumbnail removed with Jhead 1.3 or earlier\n");
924 }else{
925 ErrNonfatal("Illegal subdirectory link",0,0);
926 }
927 }else{
928 if (SubdirStart <= OffsetBase+ExifLength){
929 if (ShowTags) printf("%s Continued directory ",IndentString);
930 ProcessExifDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1);
931 }
932 }
933 if (Offset > ImageInfo.LargestExifOffset){
934 ImageInfo.LargestExifOffset = Offset;
935 }
936 }
937 }else{
938 // The exif header ends before the last next directory pointer.
939 }
940 }
941
942 if (ThumbnailOffset){
943 ImageInfo.ThumbnailAtEnd = FALSE;
944
945 if (DumpExifMap){
946 printf("Map: %05d-%05d: Thumbnail\n",ThumbnailOffset, ThumbnailOffset+ThumbnailSize);
947 }
948
949 if (ThumbnailOffset <= ExifLength){
950 if (ThumbnailSize > ExifLength-ThumbnailOffset){
951 // If thumbnail extends past exif header, only save the part that
952 // actually exists. Canon's EOS viewer utility will do this - the
953 // thumbnail extracts ok with this hack.
954 ThumbnailSize = ExifLength-ThumbnailOffset;
955 if (ShowTags) printf("Thumbnail incorrectly placed in header\n");
956
957 }
958 // The thumbnail pointer appears to be valid. Store it.
959 ImageInfo.ThumbnailOffset = ThumbnailOffset;
960 ImageInfo.ThumbnailSize = ThumbnailSize;
961
962 if (ShowTags){
963 printf("Thumbnail size: %d bytes\n",ThumbnailSize);
964 }
965 }
966 }
967 printf("returning from ProcessExifDir");
968}
969
970
971//--------------------------------------------------------------------------
972// Process a EXIF marker
973// Describes all the drivel that most digital cameras include...
974//--------------------------------------------------------------------------
975void process_EXIF (unsigned char * ExifSection, unsigned int length)
976{
977 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 // Usually set to 8, but other values valid too.
1018 ErrNonfatal("Suspicious offset of first IFD value",0,0);
1019 return;
1020 }
1021
1022 DirWithThumbnailPtrs = NULL;
1023
1024
1025 // First directory starts 16 bytes in. All offset are relative to 8 bytes in.
1026 ProcessExifDir(ExifSection+8+FirstOffset, ExifSection+8, length-8, 0);
1027
1028 ImageInfo.ThumbnailAtEnd = ImageInfo.ThumbnailOffset >= ImageInfo.LargestExifOffset ? TRUE : FALSE;
1029#ifdef SUPERDEBUG
1030 printf("Thumbnail %s end", (ImageInfo.ThumbnailAtEnd ? "at" : "NOT at"));
1031#endif
1032 if (DumpExifMap){
1033 unsigned a,b;
1034 printf("Map: %05d- End of exif\n",length-8);
1035// for (a=0;a<length-8;a+= 10){
1036// printf("Map: %05d ",a);
1037// for (b=0;b<10;b++) printf(" %02x",*(ExifSection+8+a+b));
1038// printf("\n");
1039// }
1040 for (a = 0; a < length - 8; ++a) {
1041 unsigned char c = *(ExifSection+8+a);
1042 unsigned pc = isprint(c) ? c : ' ';
1043 printf("Map: %4d %02x %c", a, c, pc);
1044 }
1045 }
1046
1047
1048 // Compute the CCD width, in millimeters.
1049 if (FocalplaneXRes != 0){
1050 // Note: With some cameras, its not possible to compute this correctly because
1051 // they don't adjust the indicated focal plane resolution units when using less
1052 // than maximum resolution, so the CCDWidth value comes out too small. Nothing
1053 // that Jhad can do about it - its a camera problem.
1054 ImageInfo.CCDWidth = (float)(ExifImageWidth * FocalplaneUnits / FocalplaneXRes);
1055
Wu-cheng Li574d52d2010-01-31 22:34:33 +08001056 if (ImageInfo.FocalLength.num != 0 && ImageInfo.FocalLength.denom != 0
1057 && ImageInfo.FocalLength35mmEquiv == 0){
The Android Open Source Project34a25642009-03-03 19:30:03 -08001058 // Compute 35 mm equivalent focal length based on sensor geometry if we haven't
1059 // already got it explicitly from a tag.
Wu-cheng Li574d52d2010-01-31 22:34:33 +08001060 ImageInfo.FocalLength35mmEquiv = (int)(
1061 (double)ImageInfo.FocalLength.num / ImageInfo.FocalLength.denom
1062 / ImageInfo.CCDWidth * 36 + 0.5);
The Android Open Source Project34a25642009-03-03 19:30:03 -08001063 }
1064 }
1065}
1066
1067static const TagTable_t* TagToTagTableEntry(unsigned short tag)
1068{
1069 unsigned int i;
1070 for (i = 0; i < TAG_TABLE_SIZE; i++) {
1071 if (TagTable[i].Tag == tag) {
1072 printf("found tag %d", tag);
1073 int format = TagTable[i].Format;
1074 if (format == 0) {
1075 printf("tag %s format not defined ***** YOU MUST ADD THE FORMAT TO THE TagTable in exif.c!!!!", TagTable[i].Desc);
1076 return NULL;
1077 }
1078 return &TagTable[i];
1079 }
1080 }
1081 printf("tag %d NOT FOUND", tag);
1082 return NULL;
1083}
1084
1085static void writeExifTagAndData(int tag,
1086 int format,
1087 long components,
1088 long value,
1089 int valueInString,
1090 char* Buffer,
1091 int* DirIndex,
1092 int* DataWriteIndex) {
Tyler Luu75407802011-10-19 01:36:15 -05001093 void* componentsPosition = NULL; // for saving component position
1094
The Android Open Source Project34a25642009-03-03 19:30:03 -08001095 Put16u(Buffer+ (*DirIndex), tag); // Tag
1096 Put16u(Buffer+(*DirIndex) + 2, format); // Format
1097 if (format == FMT_STRING && components == -1) {
1098 components = strlen((char*)value) + 1; // account for null terminator
1099 if (components & 1) ++components; // no odd lengths
1100 }
Tyler Luubd900942011-10-12 17:46:32 -05001101 if (format == FMT_UNDEFINED && components == -1) {
1102 // check if this UNDEFINED format is actually ASCII (as it usually is)
1103 // if so, we can calculate the size
1104 if(memcmp((char*)value, ExifAsciiPrefix, sizeof(ExifAsciiPrefix)) == 0) {
1105 components = sizeof(ExifAsciiPrefix) +
1106 strlen((char*)value + sizeof(ExifAsciiPrefix)) + 1;
1107 if (components & 1) ++components; // no odd lengths
1108 }
1109 }
The Android Open Source Project34a25642009-03-03 19:30:03 -08001110 Put32u(Buffer+(*DirIndex) + 4, components); // Components
Tyler Luu75407802011-10-19 01:36:15 -05001111 componentsPosition = Buffer+(*DirIndex) + 4; // components # can change for lists
The Android Open Source Project34a25642009-03-03 19:30:03 -08001112 printf("# components: %ld", components);
1113 if (format == FMT_STRING) {
1114 // short strings can fit right in the long, otherwise have to
1115 // go in the data area
1116 if (components <= 4) {
1117 strcpy(Buffer+(*DirIndex) + 8, (char*)value);
1118 } else {
1119 Put32u(Buffer+(*DirIndex) + 8, (*DataWriteIndex)-8); // Pointer
1120 printf("copying value %s to %d", (char*)value, (*DataWriteIndex));
1121 strncpy(Buffer+(*DataWriteIndex), (char*)value, components);
1122 (*DataWriteIndex) += components;
1123 }
Tyler Luubd900942011-10-12 17:46:32 -05001124 } else if ((format == FMT_UNDEFINED) &&
1125 (memcmp((char*)value, ExifAsciiPrefix, sizeof(ExifAsciiPrefix)) == 0)) {
1126 // short strings can fit right in the long, otherwise have to
1127 // go in the data area
1128 if (components <= 4) {
1129 memcpy(Buffer+(*DirIndex) + 8, (char*)value, components);
1130 } else {
1131 Put32u(Buffer+(*DirIndex) + 8, (*DataWriteIndex)-8); // Pointer
1132 printf("copying %s to %d", (char*)value + sizeof(ExifAsciiPrefix), (*DataWriteIndex));
1133 memcpy(Buffer+(*DataWriteIndex), (char*)value, components);
1134 (*DataWriteIndex) += components;
1135 }
The Android Open Source Project34a25642009-03-03 19:30:03 -08001136 } else if (!valueInString) {
1137 Put32u(Buffer+(*DirIndex) + 8, value); // Value
1138 } else {
1139 Put32u(Buffer+(*DirIndex) + 8, (*DataWriteIndex)-8); // Pointer
1140 char* curElement = strtok((char*)value, ",");
1141 int i;
Tyler Luu658ad712011-11-17 19:34:29 -06001142
1143 // (components == -1) Need to handle lists with unknown length too
1144 for (i = 0; ((i < components) || (components == -1)) && curElement != NULL; i++) {
The Android Open Source Project34a25642009-03-03 19:30:03 -08001145#ifdef SUPERDEBUG
1146 printf("processing component %s format %s", curElement, formatStr(format));
1147#endif
1148 // elements are separated by commas
1149 if (format == FMT_URATIONAL) {
1150 char* separator = strchr(curElement, '/');
1151 if (separator) {
1152 unsigned int numerator = atoi(curElement);
1153 unsigned int denominator = atoi(separator + 1);
1154 Put32u(Buffer+(*DataWriteIndex), numerator);
1155 Put32u(Buffer+(*DataWriteIndex) + 4, denominator);
1156 (*DataWriteIndex) += 8;
1157 }
1158 } else if (format == FMT_SRATIONAL) {
1159 char* separator = strchr(curElement, '/');
1160 if (separator) {
1161 int numerator = atoi(curElement);
1162 int denominator = atoi(separator + 1);
1163 Put32u(Buffer+(*DataWriteIndex), numerator);
1164 Put32u(Buffer+(*DataWriteIndex) + 4, denominator);
1165 (*DataWriteIndex) += 8;
1166 }
Tyler Luu658ad712011-11-17 19:34:29 -06001167 } else if ((components == -1) && ((format == FMT_USHORT) || (format == FMT_SSHORT))) {
1168 // variable components need to go into data write area
1169 value = atoi(curElement);
1170 Put16u(Buffer+(*DataWriteIndex), value);
1171 (*DataWriteIndex) += 4;
The Android Open Source Project34a25642009-03-03 19:30:03 -08001172 } else {
1173 // TODO: doesn't handle multiple components yet -- if more than one, have to put in data write area.
1174 value = atoi(curElement);
1175 Put32u(Buffer+(*DirIndex) + 8, value); // Value
1176 }
1177 curElement = strtok(NULL, ",");
1178 }
Tyler Luu75407802011-10-19 01:36:15 -05001179 if (components == -1) Put32u(componentsPosition, i); // update component # for unknowns
The Android Open Source Project34a25642009-03-03 19:30:03 -08001180 }
1181 (*DirIndex) += 12;
1182}
1183
1184#ifdef SUPERDEBUG
1185char* formatStr(int format) {
1186 switch (format) {
1187 case FMT_BYTE: return "FMT_BYTE"; break;
1188 case FMT_STRING: return "FMT_STRING"; break;
1189 case FMT_USHORT: return "FMT_USHORT"; break;
1190 case FMT_ULONG: return "FMT_ULONG"; break;
1191 case FMT_URATIONAL: return "FMT_URATIONAL"; break;
1192 case FMT_SBYTE: return "FMT_SBYTE"; break;
1193 case FMT_UNDEFINED: return "FMT_UNDEFINED"; break;
1194 case FMT_SSHORT: return "FMT_SSHORT"; break;
1195 case FMT_SLONG: return "FMT_SLONG"; break;
1196 case FMT_SRATIONAL: return "FMT_SRATIONAL"; break;
1197 case FMT_SINGLE: return "FMT_SINGLE"; break;
1198 case FMT_DOUBLE: return "FMT_SINGLE"; break;
1199 default: return "UNKNOWN";
1200 }
1201}
1202#endif
1203
1204//--------------------------------------------------------------------------
1205// Create minimal exif header - just date and thumbnail pointers,
1206// so that date and thumbnail may be filled later.
1207//--------------------------------------------------------------------------
Angus Kong482486a2012-01-19 15:56:10 +08001208static void create_EXIF_internal(ExifElement_t* elements, int exifTagCount, int gpsTagCount, int hasDateTimeTag, char* Buffer)
The Android Open Source Project34a25642009-03-03 19:30:03 -08001209{
The Android Open Source Project34a25642009-03-03 19:30:03 -08001210 unsigned short NumEntries;
1211 int DataWriteIndex;
1212 int DirIndex;
Tyler Luu0aba81d2011-09-27 15:47:22 -05001213 int DirExifLink = 0;
The Android Open Source Project34a25642009-03-03 19:30:03 -08001214
1215#ifdef SUPERDEBUG
Steve Block7a314da2012-01-06 19:10:19 +00001216 ALOGE("create_EXIF %d exif elements, %d gps elements", exifTagCount, gpsTagCount);
The Android Open Source Project34a25642009-03-03 19:30:03 -08001217#endif
Chih-Chung Changd6a02c32009-03-24 20:34:28 -07001218
The Android Open Source Project34a25642009-03-03 19:30:03 -08001219 MotorolaOrder = 0;
1220
1221 memcpy(Buffer+2, "Exif\0\0II",8);
1222 Put16u(Buffer+10, 0x2a);
1223
1224 DataWriteIndex = 16;
1225 Put32u(Buffer+12, DataWriteIndex-8); // first IFD offset. Means start 16 bytes in.
1226
1227 {
1228 DirIndex = DataWriteIndex;
Angus Kong482486a2012-01-19 15:56:10 +08001229 NumEntries = 1 + exifTagCount; // the extra is the thumbnail
The Android Open Source Project34a25642009-03-03 19:30:03 -08001230 if (gpsTagCount) {
1231 ++NumEntries; // allow for the GPS info tag
1232 }
Angus Kong482486a2012-01-19 15:56:10 +08001233 if (!hasDateTimeTag) {
1234 // We have to write extra date time tag. The entry number should be
1235 // adjusted.
1236 ++NumEntries;
1237 }
The Android Open Source Project34a25642009-03-03 19:30:03 -08001238 DataWriteIndex += 2 + NumEntries*12 + 4;
1239
1240 Put16u(Buffer+DirIndex, NumEntries); // Number of entries
1241 DirIndex += 2;
Chih-Chung Changd6a02c32009-03-24 20:34:28 -07001242
1243 // Entries go here...
Angus Kong482486a2012-01-19 15:56:10 +08001244 if (!hasDateTimeTag) {
The Android Open Source Project34a25642009-03-03 19:30:03 -08001245 // Date/time entry
1246 char* dateTime = NULL;
1247 char dateBuf[20];
Angus Kong482486a2012-01-19 15:56:10 +08001248 if (ImageInfo.numDateTimeTags) {
The Android Open Source Project34a25642009-03-03 19:30:03 -08001249 // If we had a pre-existing exif header, use time from that.
1250 dateTime = ImageInfo.DateTime;
1251 } else {
1252 // Oterwise, use the file's timestamp.
1253 FileTimeAsString(dateBuf);
1254 dateTime = dateBuf;
1255 }
1256 writeExifTagAndData(TAG_DATETIME,
1257 FMT_STRING,
1258 20,
1259 (long)(char*)dateBuf,
1260 FALSE,
1261 Buffer,
1262 &DirIndex,
1263 &DataWriteIndex);
1264
1265 }
1266 if (exifTagCount > 0) {
1267 int i;
1268 for (i = 0; i < exifTagCount + gpsTagCount; i++) {
1269 if (elements[i].GpsTag) {
1270 continue;
1271 }
1272 const TagTable_t* entry = TagToTagTableEntry(elements[i].Tag);
1273 if (entry == NULL) {
1274 continue;
1275 }
1276#ifdef SUPERDEBUG
Steve Block7a314da2012-01-06 19:10:19 +00001277 ALOGE("create_EXIF saving tag %x value \"%s\"",elements[i].Tag, elements[i].Value);
The Android Open Source Project34a25642009-03-03 19:30:03 -08001278#endif
1279 writeExifTagAndData(elements[i].Tag,
1280 entry->Format,
1281 entry->DataLength,
1282 (long)elements[i].Value,
1283 TRUE,
1284 Buffer,
1285 &DirIndex,
1286 &DataWriteIndex);
1287 }
Angus Kong482486a2012-01-19 15:56:10 +08001288
The Android Open Source Project34a25642009-03-03 19:30:03 -08001289 if (gpsTagCount) {
1290 // Link to gps dir entry
1291 writeExifTagAndData(TAG_GPSINFO,
1292 FMT_ULONG,
1293 1,
1294 DataWriteIndex-8,
1295 FALSE,
1296 Buffer,
1297 &DirIndex,
1298 &DataWriteIndex);
1299 }
1300
1301 // Link to exif dir entry
1302 int exifDirPtr = DataWriteIndex-8;
1303 if (gpsTagCount) {
1304 exifDirPtr += 2 + gpsTagCount*12 + 4;
1305 }
Tyler Luu0aba81d2011-09-27 15:47:22 -05001306 DirExifLink = DirIndex;
The Android Open Source Project34a25642009-03-03 19:30:03 -08001307 writeExifTagAndData(TAG_EXIF_OFFSET,
1308 FMT_ULONG,
1309 1,
1310 exifDirPtr,
1311 FALSE,
1312 Buffer,
1313 &DirIndex,
1314 &DataWriteIndex);
1315 }
1316
1317 // End of directory - contains optional link to continued directory.
Tyler Luu0aba81d2011-09-27 15:47:22 -05001318 Put32u(Buffer+DirIndex, 0);
The Android Open Source Project34a25642009-03-03 19:30:03 -08001319 printf("Ending Exif section DirIndex = %d DataWriteIndex %d", DirIndex, DataWriteIndex);
1320 }
1321
Chih-Chung Changd6a02c32009-03-24 20:34:28 -07001322
The Android Open Source Project34a25642009-03-03 19:30:03 -08001323 // GPS Section
1324 if (gpsTagCount) {
1325 DirIndex = DataWriteIndex;
1326 printf("Starting GPS section DirIndex = %d", DirIndex);
1327 NumEntries = gpsTagCount;
1328 DataWriteIndex += 2 + NumEntries*12 + 4;
1329
1330 Put16u(Buffer+DirIndex, NumEntries); // Number of entries
1331 DirIndex += 2;
1332 {
1333 int i;
1334 for (i = 0; i < exifTagCount + gpsTagCount; i++) {
1335 if (!elements[i].GpsTag) {
1336 continue;
1337 }
1338 const TagTable_t* entry = GpsTagToTagTableEntry(elements[i].Tag);
1339 if (entry == NULL) {
1340 continue;
1341 }
1342#ifdef SUPERDEBUG
Steve Block7a314da2012-01-06 19:10:19 +00001343 ALOGE("create_EXIF saving GPS tag %x value \"%s\"",elements[i].Tag, elements[i].Value);
The Android Open Source Project34a25642009-03-03 19:30:03 -08001344#endif
1345 writeExifTagAndData(elements[i].Tag,
1346 entry->Format,
1347 entry->DataLength,
1348 (long)elements[i].Value,
1349 TRUE,
1350 Buffer,
1351 &DirIndex,
1352 &DataWriteIndex);
1353 }
1354 }
1355
1356 // End of directory - contains optional link to continued directory.
1357 Put32u(Buffer+DirIndex, 0);
1358 printf("Ending GPS section DirIndex = %d DataWriteIndex %d", DirIndex, DataWriteIndex);
1359 }
1360
1361 {
Tyler Luu0aba81d2011-09-27 15:47:22 -05001362 // Overwriting TAG_EXIF_OFFSET which links to this directory
1363 Put32u(Buffer+DirExifLink+8, DataWriteIndex-8);
The Android Open Source Project34a25642009-03-03 19:30:03 -08001364
1365 printf("Starting Thumbnail section DirIndex = %d", DirIndex);
1366 DirIndex = DataWriteIndex;
1367 NumEntries = 2;
1368 DataWriteIndex += 2 + NumEntries*12 + 4;
1369
1370 Put16u(Buffer+DirIndex, NumEntries); // Number of entries
1371 DirIndex += 2;
1372 {
1373 // Link to exif dir entry
1374 writeExifTagAndData(TAG_THUMBNAIL_OFFSET,
1375 FMT_ULONG,
1376 1,
1377 DataWriteIndex-8,
1378 FALSE,
1379 Buffer,
1380 &DirIndex,
1381 &DataWriteIndex);
1382 }
1383
1384 {
1385 // Link to exif dir entry
1386 writeExifTagAndData(TAG_THUMBNAIL_LENGTH,
1387 FMT_ULONG,
1388 1,
1389 0,
1390 FALSE,
1391 Buffer,
1392 &DirIndex,
1393 &DataWriteIndex);
1394 }
1395
1396 // End of directory - contains optional link to continued directory.
1397 Put32u(Buffer+DirIndex, 0);
1398 printf("Ending Thumbnail section DirIndex = %d DataWriteIndex %d", DirIndex, DataWriteIndex);
1399 }
1400
Chih-Chung Changd6a02c32009-03-24 20:34:28 -07001401
The Android Open Source Project34a25642009-03-03 19:30:03 -08001402 Buffer[0] = (unsigned char)(DataWriteIndex >> 8);
1403 Buffer[1] = (unsigned char)DataWriteIndex;
1404
1405 // Remove old exif section, if there was one.
1406 RemoveSectionType(M_EXIF);
1407
1408 {
1409 // Sections need malloced buffers, so do that now, especially because
1410 // we now know how big it needs to be allocated.
1411 unsigned char * NewBuf = malloc(DataWriteIndex);
1412 if (NewBuf == NULL){
1413 ErrFatal("Could not allocate memory");
1414 }
1415 memcpy(NewBuf, Buffer, DataWriteIndex);
1416
1417 CreateSection(M_EXIF, NewBuf, DataWriteIndex);
1418
1419 // Re-parse new exif section, now that its in place
1420 // otherwise, we risk touching data that has already been freed.
1421 process_EXIF(NewBuf, DataWriteIndex);
1422 }
1423}
1424
Angus Kong482486a2012-01-19 15:56:10 +08001425void create_EXIF(ExifElement_t* elements, int exifTagCount, int gpsTagCount, int hasDateTimeTag)
Takahiro Okada2645b482011-01-27 20:06:00 +09001426{
1427 // It is hard to calculate exact necessary size for editing the exif
1428 // header dynamically, so we are using the maximum size of EXIF, 64K
1429 const int EXIF_MAX_SIZE = 1024*64;
1430 char* Buffer = malloc(EXIF_MAX_SIZE);
1431
1432 if (Buffer != NULL) {
Angus Kong482486a2012-01-19 15:56:10 +08001433 create_EXIF_internal(elements, exifTagCount, gpsTagCount, hasDateTimeTag, Buffer);
Takahiro Okada2645b482011-01-27 20:06:00 +09001434 free(Buffer);
1435 } else {
1436 ErrFatal("Could not allocate memory");
1437 }
1438}
1439
The Android Open Source Project34a25642009-03-03 19:30:03 -08001440//--------------------------------------------------------------------------
1441// Cler the rotation tag in the exif header to 1.
1442//--------------------------------------------------------------------------
1443const char * ClearOrientation(void)
1444{
1445 int a;
1446 if (NumOrientations == 0) return NULL;
1447
1448 for (a=0;a<NumOrientations;a++){
1449 switch(OrientationNumFormat[a]){
1450 case FMT_SBYTE:
1451 case FMT_BYTE:
1452 *(uchar *)(OrientationPtr[a]) = 1;
1453 break;
1454
1455 case FMT_USHORT:
1456 Put16u(OrientationPtr[a], 1);
1457 break;
1458
1459 case FMT_ULONG:
1460 case FMT_SLONG:
1461 memset(OrientationPtr, 0, 4);
1462 // Can't be bothered to write generic Put32 if I only use it once.
1463 if (MotorolaOrder){
1464 ((uchar *)OrientationPtr[a])[3] = 1;
1465 }else{
1466 ((uchar *)OrientationPtr[a])[0] = 1;
1467 }
1468 break;
1469
1470 default:
1471 return NULL;
1472 }
1473 }
1474
1475 return OrientTab[ImageInfo.Orientation];
1476}
1477
1478
1479
1480//--------------------------------------------------------------------------
1481// Remove thumbnail out of the exif image.
1482//--------------------------------------------------------------------------
1483int RemoveThumbnail(unsigned char * ExifSection)
1484{
1485 if (!DirWithThumbnailPtrs ||
1486 ImageInfo.ThumbnailOffset == 0 ||
1487 ImageInfo.ThumbnailSize == 0){
1488 // No thumbnail, or already deleted it.
1489 return 0;
1490 }
1491 if (ImageInfo.ThumbnailAtEnd == FALSE){
1492 ErrNonfatal("Thumbnail is not at end of header, can't chop it off", 0, 0);
1493 return 0;
1494 }
1495
1496 {
1497 int de;
1498 int NumDirEntries;
1499 NumDirEntries = Get16u(DirWithThumbnailPtrs);
1500
1501 for (de=0;de<NumDirEntries;de++){
1502 int Tag;
1503 unsigned char * DirEntry;
1504 DirEntry = DIR_ENTRY_ADDR(DirWithThumbnailPtrs, de);
1505 Tag = Get16u(DirEntry);
1506 if (Tag == TAG_THUMBNAIL_LENGTH){
1507 // Set length to zero.
1508 if (Get16u(DirEntry+2) != FMT_ULONG){
1509 // non standard format encoding. Can't do it.
1510 ErrNonfatal("Can't remove thumbnail", 0, 0);
1511 return 0;
1512 }
1513 Put32u(DirEntry+8, 0);
1514 }
1515 }
1516 }
1517
1518 // This is how far the non thumbnail data went.
1519 return ImageInfo.ThumbnailOffset+8;
1520
1521}
1522
1523
1524//--------------------------------------------------------------------------
1525// Convert exif time to Unix time structure
1526//--------------------------------------------------------------------------
1527int Exif2tm(struct tm * timeptr, char * ExifTime)
1528{
1529 int a;
1530
1531 timeptr->tm_wday = -1;
1532
1533 // Check for format: YYYY:MM:DD HH:MM:SS format.
1534 // Date and time normally separated by a space, but also seen a ':' there, so
1535 // skip the middle space with '%*c' so it can be any character.
1536 a = sscanf(ExifTime, "%d%*c%d%*c%d%*c%d:%d:%d",
1537 &timeptr->tm_year, &timeptr->tm_mon, &timeptr->tm_mday,
1538 &timeptr->tm_hour, &timeptr->tm_min, &timeptr->tm_sec);
1539
1540
1541 if (a == 6){
1542 timeptr->tm_isdst = -1;
1543 timeptr->tm_mon -= 1; // Adjust for unix zero-based months
1544 timeptr->tm_year -= 1900; // Adjust for year starting at 1900
1545 return TRUE; // worked.
1546 }
1547
1548 return FALSE; // Wasn't in Exif date format.
1549}
1550
1551
1552//--------------------------------------------------------------------------
1553// Show the collected image info, displaying camera F-stop and shutter speed
1554// in a consistent and legible fashion.
1555//--------------------------------------------------------------------------
1556void ShowImageInfo(int ShowFileInfo)
1557{
1558 if (ShowFileInfo){
1559 printf("File name : %s\n",ImageInfo.FileName);
1560 printf("File size : %d bytes\n",ImageInfo.FileSize);
1561
1562 {
1563 char Temp[20];
1564 FileTimeAsString(Temp);
1565 printf("File date : %s\n",Temp);
1566 }
1567 }
1568
1569 if (ImageInfo.CameraMake[0]){
1570 printf("Camera make : %s\n",ImageInfo.CameraMake);
1571 printf("Camera model : %s\n",ImageInfo.CameraModel);
1572 }
1573 if (ImageInfo.DateTime[0]){
1574 printf("Date/Time : %s\n",ImageInfo.DateTime);
1575 }
1576 printf("Resolution : %d x %d\n",ImageInfo.Width, ImageInfo.Height);
1577
1578 if (ImageInfo.Orientation > 1){
1579 // Only print orientation if one was supplied, and if its not 1 (normal orientation)
1580 printf("Orientation : %s\n", OrientTab[ImageInfo.Orientation]);
1581 }
1582
1583 if (ImageInfo.IsColor == 0){
1584 printf("Color/bw : Black and white\n");
1585 }
1586
1587 if (ImageInfo.FlashUsed >= 0){
1588 if (ImageInfo.FlashUsed & 1){
1589 printf("Flash used : Yes");
1590 switch (ImageInfo.FlashUsed){
1591 case 0x5: printf(" (Strobe light not detected)"); break;
1592 case 0x7: printf(" (Strobe light detected) "); break;
1593 case 0x9: printf(" (manual)"); break;
1594 case 0xd: printf(" (manual, return light not detected)"); break;
1595 case 0xf: printf(" (manual, return light detected)"); break;
1596 case 0x19:printf(" (auto)"); break;
1597 case 0x1d:printf(" (auto, return light not detected)"); break;
1598 case 0x1f:printf(" (auto, return light detected)"); break;
1599 case 0x41:printf(" (red eye reduction mode)"); break;
1600 case 0x45:printf(" (red eye reduction mode return light not detected)"); break;
1601 case 0x47:printf(" (red eye reduction mode return light detected)"); break;
1602 case 0x49:printf(" (manual, red eye reduction mode)"); break;
1603 case 0x4d:printf(" (manual, red eye reduction mode, return light not detected)"); break;
1604 case 0x4f:printf(" (red eye reduction mode, return light detected)"); break;
1605 case 0x59:printf(" (auto, red eye reduction mode)"); break;
1606 case 0x5d:printf(" (auto, red eye reduction mode, return light not detected)"); break;
1607 case 0x5f:printf(" (auto, red eye reduction mode, return light detected)"); break;
1608 }
1609 }else{
1610 printf("Flash used : No");
1611 switch (ImageInfo.FlashUsed){
1612 case 0x18:printf(" (auto)"); break;
1613 }
1614 }
1615 printf("\n");
1616 }
1617
1618
Wu-cheng Li574d52d2010-01-31 22:34:33 +08001619 if (ImageInfo.FocalLength.num != 0 && ImageInfo.FocalLength.denom != 0) {
1620 printf("Focal length : %4.1fmm",(double)ImageInfo.FocalLength.num / ImageInfo.FocalLength.denom);
The Android Open Source Project34a25642009-03-03 19:30:03 -08001621 if (ImageInfo.FocalLength35mmEquiv){
1622 printf(" (35mm equivalent: %dmm)", ImageInfo.FocalLength35mmEquiv);
1623 }
1624 printf("\n");
1625 }
1626
1627 if (ImageInfo.DigitalZoomRatio > 1){
1628 // Digital zoom used. Shame on you!
1629 printf("Digital Zoom : %1.3fx\n", (double)ImageInfo.DigitalZoomRatio);
1630 }
1631
1632 if (ImageInfo.CCDWidth){
1633 printf("CCD width : %4.2fmm\n",(double)ImageInfo.CCDWidth);
1634 }
1635
1636 if (ImageInfo.ExposureTime){
1637 if (ImageInfo.ExposureTime < 0.010){
1638 printf("Exposure time: %6.4f s ",(double)ImageInfo.ExposureTime);
1639 }else{
1640 printf("Exposure time: %5.3f s ",(double)ImageInfo.ExposureTime);
1641 }
1642 if (ImageInfo.ExposureTime <= 0.5){
1643 printf(" (1/%d)",(int)(0.5 + 1/ImageInfo.ExposureTime));
1644 }
1645 printf("\n");
1646 }
1647 if (ImageInfo.ApertureFNumber){
1648 printf("Aperture : f/%3.1f\n",(double)ImageInfo.ApertureFNumber);
1649 }
1650 if (ImageInfo.Distance){
1651 if (ImageInfo.Distance < 0){
1652 printf("Focus dist. : Infinite\n");
1653 }else{
1654 printf("Focus dist. : %4.2fm\n",(double)ImageInfo.Distance);
1655 }
1656 }
1657
1658 if (ImageInfo.ISOequivalent){
1659 printf("ISO equiv. : %2d\n",(int)ImageInfo.ISOequivalent);
1660 }
1661
1662 if (ImageInfo.ExposureBias){
1663 // If exposure bias was specified, but set to zero, presumably its no bias at all,
1664 // so only show it if its nonzero.
1665 printf("Exposure bias: %4.2f\n",(double)ImageInfo.ExposureBias);
1666 }
1667
1668 switch(ImageInfo.Whitebalance) {
1669 case 1:
1670 printf("Whitebalance : Manual\n");
1671 break;
1672 case 0:
1673 printf("Whitebalance : Auto\n");
1674 break;
1675 }
1676
1677 //Quercus: 17-1-2004 Added LightSource, some cams return this, whitebalance or both
1678 switch(ImageInfo.LightSource) {
1679 case 1:
1680 printf("Light Source : Daylight\n");
1681 break;
1682 case 2:
1683 printf("Light Source : Fluorescent\n");
1684 break;
1685 case 3:
1686 printf("Light Source : Incandescent\n");
1687 break;
1688 case 4:
1689 printf("Light Source : Flash\n");
1690 break;
1691 case 9:
1692 printf("Light Source : Fine weather\n");
1693 break;
1694 case 11:
1695 printf("Light Source : Shade\n");
1696 break;
1697 default:; //Quercus: 17-1-2004 There are many more modes for this, check Exif2.2 specs
1698 // If it just says 'unknown' or we don't know it, then
1699 // don't bother showing it - it doesn't add any useful information.
1700 }
1701
1702 if (ImageInfo.MeteringMode){ // 05-jan-2001 vcs
1703 switch(ImageInfo.MeteringMode) {
1704 case 2:
1705 printf("Metering Mode: center weight\n");
1706 break;
1707 case 3:
1708 printf("Metering Mode: spot\n");
1709 break;
1710 case 5:
1711 printf("Metering Mode: matrix\n");
1712 break;
1713 }
1714 }
1715
1716 if (ImageInfo.ExposureProgram){ // 05-jan-2001 vcs
1717 switch(ImageInfo.ExposureProgram) {
1718 case 1:
1719 printf("Exposure : Manual\n");
1720 break;
1721 case 2:
1722 printf("Exposure : program (auto)\n");
1723 break;
1724 case 3:
1725 printf("Exposure : aperture priority (semi-auto)\n");
1726 break;
1727 case 4:
1728 printf("Exposure : shutter priority (semi-auto)\n");
1729 break;
1730 case 5:
1731 printf("Exposure : Creative Program (based towards depth of field)\n");
1732 break;
1733 case 6:
1734 printf("Exposure : Action program (based towards fast shutter speed)\n");
1735 break;
1736 case 7:
1737 printf("Exposure : Portrait Mode\n");
1738 break;
1739 case 8:
1740 printf("Exposure : LandscapeMode \n");
1741 break;
1742 default:
1743 break;
1744 }
1745 }
1746 switch(ImageInfo.ExposureMode){
1747 case 0: // Automatic (not worth cluttering up output for)
1748 break;
1749 case 1: printf("Exposure Mode: Manual\n");
1750 break;
1751 case 2: printf("Exposure Mode: Auto bracketing\n");
1752 break;
1753 }
1754
Chih-Chung Changd6a02c32009-03-24 20:34:28 -07001755 if (ImageInfo.DistanceRange) {
1756 printf("Focus range : ");
1757 switch(ImageInfo.DistanceRange) {
1758 case 1:
1759 printf("macro");
1760 break;
1761 case 2:
1762 printf("close");
1763 break;
1764 case 3:
1765 printf("distant");
1766 break;
1767 }
1768 printf("\n");
1769 }
1770
1771
The Android Open Source Project34a25642009-03-03 19:30:03 -08001772
1773 if (ImageInfo.Process != M_SOF0){
1774 // don't show it if its the plain old boring 'baseline' process, but do
1775 // show it if its something else, like 'progressive' (used on web sometimes)
1776 int a;
1777 for (a=0;;a++){
1778 if (a >= (int)PROCESS_TABLE_SIZE){
1779 // ran off the end of the table.
1780 printf("Jpeg process : Unknown\n");
1781 break;
1782 }
1783 if (ProcessTable[a].Tag == ImageInfo.Process){
1784 printf("Jpeg process : %s\n",ProcessTable[a].Desc);
1785 break;
1786 }
1787 }
1788 }
1789
1790 if (ImageInfo.GpsInfoPresent){
1791 printf("GPS Latitude : %s\n",ImageInfo.GpsLat);
1792 printf("GPS Longitude: %s\n",ImageInfo.GpsLong);
1793 if (ImageInfo.GpsAlt[0]) printf("GPS Altitude : %s\n",ImageInfo.GpsAlt);
1794 }
1795
1796 // Print the comment. Print 'Comment:' for each new line of comment.
1797 if (ImageInfo.Comments[0]){
1798 int a,c;
1799 printf("Comment : ");
Chih-Chung Changd6a02c32009-03-24 20:34:28 -07001800 if (!ImageInfo.CommentWidchars){
1801 for (a=0;a<MAX_COMMENT_SIZE;a++){
1802 c = ImageInfo.Comments[a];
1803 if (c == '\0') break;
1804 if (c == '\n'){
1805 // Do not start a new line if the string ends with a carriage return.
1806 if (ImageInfo.Comments[a+1] != '\0'){
1807 printf("\nComment : ");
1808 }else{
1809 printf("\n");
1810 }
The Android Open Source Project34a25642009-03-03 19:30:03 -08001811 }else{
Chih-Chung Changd6a02c32009-03-24 20:34:28 -07001812 putchar(c);
The Android Open Source Project34a25642009-03-03 19:30:03 -08001813 }
The Android Open Source Project34a25642009-03-03 19:30:03 -08001814 }
Chih-Chung Changd6a02c32009-03-24 20:34:28 -07001815 printf("\n");
1816 }else{
1817 printf("%.*ls\n", ImageInfo.CommentWidchars, (wchar_t *)ImageInfo.Comments);
The Android Open Source Project34a25642009-03-03 19:30:03 -08001818 }
The Android Open Source Project34a25642009-03-03 19:30:03 -08001819 }
1820 if (ImageInfo.ThumbnailOffset){
1821 printf("Map: %05d-%05d: Thumbnail\n",ImageInfo.ThumbnailOffset, ImageInfo.ThumbnailOffset+ImageInfo.ThumbnailSize);
1822 } else {
1823 printf("NO thumbnail");
1824 }
1825}
1826
1827
1828//--------------------------------------------------------------------------
1829// Summarize highlights of image info on one line (suitable for grep-ing)
1830//--------------------------------------------------------------------------
1831void ShowConciseImageInfo(void)
1832{
1833 printf("\"%s\"",ImageInfo.FileName);
1834
1835 printf(" %dx%d",ImageInfo.Width, ImageInfo.Height);
1836
1837 if (ImageInfo.ExposureTime){
1838 if (ImageInfo.ExposureTime <= 0.5){
1839 printf(" (1/%d)",(int)(0.5 + 1/ImageInfo.ExposureTime));
1840 }else{
1841 printf(" (%1.1f)",ImageInfo.ExposureTime);
1842 }
1843 }
1844
1845 if (ImageInfo.ApertureFNumber){
1846 printf(" f/%3.1f",(double)ImageInfo.ApertureFNumber);
1847 }
1848
1849 if (ImageInfo.FocalLength35mmEquiv){
1850 printf(" f(35)=%dmm",ImageInfo.FocalLength35mmEquiv);
1851 }
1852
1853 if (ImageInfo.FlashUsed >= 0 && ImageInfo.FlashUsed & 1){
1854 printf(" (flash)");
1855 }
1856
1857 if (ImageInfo.IsColor == 0){
1858 printf(" (bw)");
1859 }
1860
1861 printf("\n");
1862}