//-------------------------------------------------------------------------- // Process GPS info directory //-------------------------------------------------------------------------- void ProcessGpsInfo(unsigned char * DirStart, unsigned char * OffsetBase, unsigned ExifLength) { int de; unsigned a; int NumDirEntries; NumDirEntries = Get16u(DirStart); #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry)) if (ShowTags){ xprintf("(dir has %d entries)\n",NumDirEntries); } ImageInfo.GpsInfoPresent = TRUE; strcpy(ImageInfo.GpsLat, "? ?"); strcpy(ImageInfo.GpsLong, "? ?"); ImageInfo.GpsAlt[0] = 0; for (de=0;de<NumDirEntries;de++){ unsigned Tag, Format, Components; unsigned char * ValuePtr; int ComponentSize; unsigned ByteCount; unsigned char * DirEntry; DirEntry = DIR_ENTRY_ADDR(DirStart, de); if (DirEntry+12 > OffsetBase+ExifLength){ ErrNonfatal("GPS info directory goes past end of exif",0,0); return; } Tag = Get16u(DirEntry); Format = Get16u(DirEntry+2); Components = Get32u(DirEntry+4); if ((Format-1) >= NUM_FORMATS) { // (-1) catches illegal zero case as unsigned underflows to positive large. ErrNonfatal("Illegal number format %d for Exif gps tag %04x", Format, Tag); continue; } ComponentSize = BytesPerFormat[Format]; ByteCount = Components * ComponentSize; if (ByteCount > 4){ unsigned OffsetVal; OffsetVal = Get32u(DirEntry+8); // If its bigger than 4 bytes, the dir entry contains an offset. if (OffsetVal+ByteCount > ExifLength){ // Bogus pointer offset and / or bytecount value ErrNonfatal("Illegal value pointer for Exif gps tag %04x", Tag,0); continue; } ValuePtr = OffsetBase+OffsetVal; }else{ // 4 bytes or less and value is in the dir entry itself ValuePtr = DirEntry+8; } switch(Tag){ char FmtString[21]; char TempString[50]; double Values[3]; case TAG_GPS_LAT_REF: ImageInfo.GpsLat[0] = ValuePtr[0]; break; case TAG_GPS_LONG_REF: ImageInfo.GpsLong[0] = ValuePtr[0]; break; case TAG_GPS_LAT: case TAG_GPS_LONG: if (Format != FMT_URATIONAL){ ErrNonfatal("Inappropriate format (%d) for Exif GPS coordinates!", Format, 0); } strcpy(FmtString, "%0.0fd %0.0fm %0.0fs"); for (a=0;a<3;a++){ int den, digits; den = Get32s(ValuePtr+4+a*ComponentSize); digits = 0; while (den > 1 && digits <= 6){ den = den / 10; digits += 1; } if (digits > 6) digits = 6; FmtString[1+a*7] = (char)('2'+digits+(digits ? 1 : 0)); FmtString[3+a*7] = (char)('0'+digits); Values[a] = ConvertAnyFormat(ValuePtr+a*ComponentSize, Format); } sprintf(TempString, FmtString, Values[0], Values[1], Values[2]); if (Tag == TAG_GPS_LAT){ strncpy(ImageInfo.GpsLat+2, TempString, 29); }else{ strncpy(ImageInfo.GpsLong+2, TempString, 29); } break; case TAG_GPS_ALT_REF: ImageInfo.GpsAlt[0] = (char)(ValuePtr[0] ? '-' : ' '); break; case TAG_GPS_ALT: sprintf(ImageInfo.GpsAlt + 1, "%.2fm", ConvertAnyFormat(ValuePtr, Format)); break; } if (ShowTags){ // Show tag value. if (Tag < MAX_GPS_TAG){ xprintf(" GPS%s =", GpsTags[Tag]); }else{ // Show unknown tag xprintf(" Illegal GPS tag %04x=", Tag); } switch(Format){ case FMT_UNDEFINED: // Undefined is typically an ascii string. case FMT_STRING: // String arrays printed without function call (different from int arrays) { xprintf("\""); for (a=0;a<ByteCount;a++){ int ZeroSkipped = 0; if (ValuePtr[a] >= 32){ if (ZeroSkipped){ xprintf("?"); ZeroSkipped = 0; } putchar(ValuePtr[a]); }else{ if (ValuePtr[a] == 0){ ZeroSkipped = 1; } } } xprintf("\"\n"); } break; default: // Handle arrays of numbers later (will there ever be?) for (a=0;;){ PrintFormatNumber(ValuePtr+a*ComponentSize, Format, ByteCount); if (++a >= Components) break; xprintf(", "); } xprintf("\n"); } } } }
//-------------------------------------------------------------------------- // Process GPS info directory //-------------------------------------------------------------------------- void ProcessGpsInfo(unsigned char * DirStart, int ByteCountUnused, unsigned char * OffsetBase, unsigned ExifLength) { int de; unsigned a; int NumDirEntries; NumDirEntries = Get16u(DirStart); #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry)) if (ShowTags){ printf("(dir has %d entries)\n",NumDirEntries); } ImageInfo.GpsInfoPresent = TRUE; strcpy(ImageInfo.GpsLat, "? ?"); strcpy(ImageInfo.GpsLong, "? ?"); ImageInfo.GpsAlt[0] = 0; for (de=0;de<NumDirEntries;de++){ unsigned Tag, Format, Components; unsigned char * ValuePtr; int ComponentSize; unsigned ByteCount; unsigned char * DirEntry; DirEntry = DIR_ENTRY_ADDR(DirStart, de); if (DirEntry+12 > OffsetBase+ExifLength){ ErrNonfatal("GPS info directory goes past end of exif",0,0); return; } Tag = Get16u(DirEntry); Format = Get16u(DirEntry+2); Components = Get32u(DirEntry+4); if ((Format-1) >= NUM_FORMATS) { // (-1) catches illegal zero case as unsigned underflows to positive large. ErrNonfatal("Illegal number format %d for tag %04x", Format, Tag); continue; } ComponentSize = BytesPerFormat[Format]; ByteCount = Components * ComponentSize; #ifdef SUPERDEBUG printf("GPS tag %x format %s #components %d componentsize %d bytecount %d", Tag, formatStr(Format), Components, ComponentSize, ByteCount); #endif if (ByteCount > 4){ unsigned OffsetVal; OffsetVal = Get32u(DirEntry+8); // If its bigger than 4 bytes, the dir entry contains an offset. if (OffsetVal+ByteCount > ExifLength){ // Bogus pointer offset and / or bytecount value ErrNonfatal("Illegal value pointer for tag %04x", Tag,0); continue; } ValuePtr = OffsetBase+OffsetVal; }else{ // 4 bytes or less and value is in the dir entry itself ValuePtr = DirEntry+8; } switch(Tag){ char FmtString[21]; char TempString[56]; double Values[3]; case TAG_GPS_LAT_REF: ImageInfo.GpsLat[0] = ValuePtr[0]; ImageInfo.GpsLatRef[0] = ValuePtr[0]; ImageInfo.GpsLatRef[1] = '\0'; break; case TAG_GPS_LONG_REF: ImageInfo.GpsLong[0] = ValuePtr[0]; ImageInfo.GpsLongRef[0] = ValuePtr[0]; ImageInfo.GpsLongRef[1] = '\0'; break; case TAG_GPS_LAT: case TAG_GPS_LONG: if (Format != FMT_URATIONAL){ ErrNonfatal("Inappropriate format (%d) for GPS coordinates!", Format, 0); } strcpy(FmtString, "%0.0fd %0.0fm %0.0fs"); for (a=0;a<3;a++){ int den, digits; den = Get32s(ValuePtr+4+a*ComponentSize); digits = 0; while (den > 1 && digits <= 6){ den = den / 10; digits += 1; } if (digits > 6) digits = 6; FmtString[1+a*7] = (char)('2'+digits+(digits ? 1 : 0)); FmtString[3+a*7] = (char)('0'+digits); Values[a] = ConvertAnyFormat(ValuePtr+a*ComponentSize, Format); } sprintf(TempString, FmtString, Values[0], Values[1], Values[2]); if (Tag == TAG_GPS_LAT){ strncpy(ImageInfo.GpsLat+2, TempString, 29); }else{ strncpy(ImageInfo.GpsLong+2, TempString, 29); } sprintf(TempString, "%d/%d,%d/%d,%d/%d", Get32s(ValuePtr), Get32s(4+(char*)ValuePtr), Get32s(8+(char*)ValuePtr), Get32s(12+(char*)ValuePtr), Get32s(16+(char*)ValuePtr), Get32s(20+(char*)ValuePtr)); if (Tag == TAG_GPS_LAT){ strncpy(ImageInfo.GpsLatRaw, TempString, 31); }else{ strncpy(ImageInfo.GpsLongRaw, TempString, 31); } break; case TAG_GPS_ALT_REF: ImageInfo.GpsAlt[0] = (char)(ValuePtr[0] ? '-' : ' '); ImageInfo.GpsAltRef = (char)ValuePtr[0]; break; case TAG_GPS_ALT: sprintf(ImageInfo.GpsAlt + 1, "%.2fm", ConvertAnyFormat(ValuePtr, Format)); ImageInfo.GpsAltRaw.num = Get32u(ValuePtr); ImageInfo.GpsAltRaw.denom = Get32u(4+(char *)ValuePtr); break; case TAG_GPS_TIMESTAMP: snprintf(ImageInfo.GpsTimeStamp, sizeof(ImageInfo.GpsTimeStamp), "%d:%d:%d", (int) ConvertAnyFormat(ValuePtr, Format), (int) ConvertAnyFormat(ValuePtr + 8, Format), (int) ConvertAnyFormat(ValuePtr + 16, Format) ); break; case TAG_GPS_DATESTAMP: strncpy(ImageInfo.GpsDateStamp, (char*)ValuePtr, sizeof(ImageInfo.GpsDateStamp)); break; case TAG_GPS_PROCESSING_METHOD: if (ByteCount > EXIF_ASCII_PREFIX_LEN && memcmp(ValuePtr, ExifAsciiPrefix, EXIF_ASCII_PREFIX_LEN) == 0) { int length = ByteCount < GPS_PROCESSING_METHOD_LEN + EXIF_ASCII_PREFIX_LEN ? ByteCount - EXIF_ASCII_PREFIX_LEN : GPS_PROCESSING_METHOD_LEN; memcpy(ImageInfo.GpsProcessingMethod, (char*)(ValuePtr + EXIF_ASCII_PREFIX_LEN), length); ImageInfo.GpsProcessingMethod[length] = 0; } else { ALOGW("Unsupported encoding for GPSProcessingMethod"); } break; } if (ShowTags){ // Show tag value. if (Tag < MAX_GPS_TAG){ printf(" %s =", GpsTags[Tag].Desc); }else{ // Show unknown tag printf(" Illegal GPS tag %04x=", Tag); } switch(Format){ case FMT_UNDEFINED: // Undefined is typically an ascii string. case FMT_STRING: // String arrays printed without function call (different from int arrays) { printf("\""); for (a=0;a<ByteCount;a++){ int ZeroSkipped = 0; if (ValuePtr[a] >= 32){ if (ZeroSkipped){ printf("?"); ZeroSkipped = 0; } putchar(ValuePtr[a]); }else{ if (ValuePtr[a] == 0){ ZeroSkipped = 1; } } } printf("\"\n"); } break; default: // Handle arrays of numbers later (will there ever be?) for (a=0;;){ PrintFormatNumber(ValuePtr+a*ComponentSize, Format, ByteCount); if (++a >= Components) break; printf(", "); } printf("\n"); } } } }
//-------------------------------------------------------------------------- // Process exif format directory, as used by Cannon maker note //-------------------------------------------------------------------------- void ProcessCanonMakerNoteDir(unsigned char * DirStart, unsigned char * OffsetBase, unsigned ExifLength) { int de; int a; int NumDirEntries; NumDirEntries = Get16u(DirStart); #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry)) { unsigned char * DirEnd; DirEnd = DIR_ENTRY_ADDR(DirStart, NumDirEntries); if (DirEnd > (OffsetBase+ExifLength)){ ErrNonfatal("Illegally sized directory",0,0); return; } if (DumpExifMap){ printf("Map: %05td-%05td: Directory (makernote)\n",DirStart-OffsetBase, DirEnd-OffsetBase); } } if (ShowTags){ printf("(dir has %d entries)\n",NumDirEntries); } for (de=0;de<NumDirEntries;de++){ int Tag, Format, Components; unsigned char * ValuePtr; int ByteCount; unsigned char * DirEntry; DirEntry = DIR_ENTRY_ADDR(DirStart, de); Tag = Get16u(DirEntry); Format = Get16u(DirEntry+2); Components = Get32u(DirEntry+4); if ((Format-1) >= NUM_FORMATS) { // (-1) catches illegal zero case as unsigned underflows to positive large. ErrNonfatal("Illegal number format %d for tag %04x", Format, Tag); continue; } if ((unsigned)Components > 0x10000){ ErrNonfatal("Illegal number of components %d for tag %04x", Components, Tag); continue; } ByteCount = Components * BytesPerFormat[Format]; if (ByteCount > 4){ unsigned OffsetVal; OffsetVal = Get32u(DirEntry+8); // If its bigger than 4 bytes, the dir entry contains an offset. if (OffsetVal+ByteCount > ExifLength){ // Bogus pointer offset and / or bytecount value ErrNonfatal("Illegal value pointer for tag %04x", Tag,0); continue; } ValuePtr = OffsetBase+OffsetVal; if (DumpExifMap){ printf("Map: %05d-%05d: Data for makernote tag %04x\n",OffsetVal, OffsetVal+ByteCount, Tag); } }else{ // 4 bytes or less and value is in the dir entry itself ValuePtr = DirEntry+8; } if (ShowTags){ // Show tag name printf(" Canon maker tag %04x Value = ", Tag); } // Show tag value. switch(Format){ case FMT_UNDEFINED: // Undefined is typically an ascii string. case FMT_STRING: // String arrays printed without function call (different from int arrays) if (ShowTags){ printf("\""); for (a=0;a<ByteCount;a++){ int ZeroSkipped = 0; if (ValuePtr[a] >= 32){ if (ZeroSkipped){ printf("?"); ZeroSkipped = 0; } putchar(ValuePtr[a]); }else{ if (ValuePtr[a] == 0){ ZeroSkipped = 1; } } } printf("\"\n"); } break; default: if (ShowTags){ PrintFormatNumber(ValuePtr, Format, ByteCount); printf("\n"); } } if (Tag == 1 && Components > 16){ int IsoCode = Get16u(ValuePtr + 16*sizeof(unsigned short)); if (IsoCode >= 16 && IsoCode <= 24){ ImageInfo.ISOequivalent = 50 << (IsoCode-16); } } if (Tag == 4 && Format == FMT_USHORT){ if (Components > 7){ int WhiteBalance = Get16u(ValuePtr + 7*sizeof(unsigned short)); switch(WhiteBalance){ // 0=Auto, 6=Custom case 1: ImageInfo.LightSource = 1; break; // Sunny case 2: ImageInfo.LightSource = 1; break; // Cloudy case 3: ImageInfo.LightSource = 3; break; // Thungsten case 4: ImageInfo.LightSource = 2; break; // Fourescent case 5: ImageInfo.LightSource = 4; break; // Flash } } if (Components > 19 && ImageInfo.Distance <= 0) { // Inidcates the distance the autofocus camera is focused to. // Tends to be less accurate as distance increases. int temp_dist = Get16u(ValuePtr + 19*sizeof(unsigned short)); printf("temp dist=%d\n",temp_dist); if (temp_dist != 65535){ ImageInfo.Distance = (float)temp_dist/100; }else{ ImageInfo.Distance = -1 /* infinity */; } } } } }
//-------------------------------------------------------------------------- // Process one of the nested EXIF directories. //-------------------------------------------------------------------------- static void ProcessExifDir(char * DirStart, char * OffsetBase, unsigned ExifLength) { int de; int a; int NumDirEntries; NumDirEntries = Get16u(DirStart); if ((DirStart+2+NumDirEntries*12) > (OffsetBase+ExifLength)){ ErrExit("Illegally sized directory"); } if (ShowTags){ printf("Directory with %d entries\n",NumDirEntries); } for (de=0;de<NumDirEntries;de++){ int Tag, Format, Components; char * ValuePtr; int ByteCount; char * DirEntry; DirEntry = DirStart+2+12*de; Tag = Get16u(DirEntry); Format = Get16u(DirEntry+2); Components = Get32u(DirEntry+4); if ((Format-1) >= NUM_FORMATS) { // (-1) catches illegal zero case as unsigned underflows to positive large. ErrExit("Illegal format code in EXIF dir"); } ByteCount = Components * BytesPerFormat[Format]; if (ByteCount > 4){ unsigned OffsetVal; OffsetVal = Get32u(DirEntry+8); // If its bigger than 4 bytes, the dir entry contains an offset. if (OffsetVal+ByteCount > ExifLength){ // Bogus pointer offset and / or bytecount value printf("Offset %d bytes %d ExifLen %d\n",OffsetVal, ByteCount, ExifLength); ErrExit("Illegal pointer offset value in EXIF"); } ValuePtr = OffsetBase+OffsetVal; }else{ // 4 bytes or less and value is in the dir entry itself ValuePtr = DirEntry+8; } if (LastExifRefd < ValuePtr+ByteCount){ // Keep track of last byte in the exif header that was actually referenced. // That way, we know where the discardable thumbnail data begins. LastExifRefd = ValuePtr+ByteCount; } if (ShowTags){ // Show tag name for (a=0;;a++){ if (TagTable[a].Tag == 0){ printf(" Unknown Tag %04x Value = ", Tag); break; } if (TagTable[a].Tag == Tag){ printf(" %s = ",TagTable[a].Desc); break; } } // Show tag value. switch(Format){ case FMT_UNDEFINED: // Undefined is typically an ascii string. case FMT_STRING: // String arrays printed without function call (different from int arrays) printf("\""); for (a=0;a<ByteCount;a++){ if (isprint((ValuePtr)[a])){ putchar((ValuePtr)[a]); } } printf("\"\n"); break; default: // Handle arrays of numbers later (will there ever be?) PrintFormatNumber(ValuePtr, Format); } } // Extract useful components of tag switch(Tag){ case TAG_MAKE: strncpy(ImageInfo.CameraMake, ValuePtr, 31); break; case TAG_MODEL: strncpy(ImageInfo.CameraModel, ValuePtr, 39); break; case TAG_DATETIME_ORIGINAL: strncpy(ImageInfo.DateTime, ValuePtr, 19); break; case TAG_USERCOMMENT: // Olympus has this padded with trailing spaces. Remove these first. for (a=ByteCount;;){ a--; if ((ValuePtr)[a] == ' '){ (ValuePtr)[a] = '\0'; }else{ break; } if (a == 0) break; } // Copy the comment if (memcmp(ValuePtr, "ASCII",5) == 0){ for (a=5;a<10;a++){ int c; c = (ValuePtr)[a]; if (c != '\0' && c != ' '){ strncpy(ImageInfo.Comments, a+ValuePtr, 199); break; } } }else{ strncpy(ImageInfo.Comments, ValuePtr, 199); } break; case TAG_FNUMBER: // Simplest way of expressing aperture, so I trust it the most. // (overwrite previously computd value if there is one) ImageInfo.ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_APERTURE: case TAG_MAXAPERTURE: // More relevant info always comes earlier, so only use this field if we don't // have appropriate aperture information yet. if (ImageInfo.ApertureFNumber == 0){ ImageInfo.ApertureFNumber = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2)*0.5); } break; case TAG_FOCALLENGTH: // Nice digital cameras actually save the focal length as a function // of how farthey are zoomed in. ImageInfo.FocalLength = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_SUBJECT_DISTANCE: // Inidcates the distacne the autofocus camera is focused to. // Tends to be less accurate as distance increases. ImageInfo.Distance = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_EXPOSURETIME: // Simplest way of expressing exposure time, so I trust it most. // (overwrite previously computd value if there is one) ImageInfo.ExposureTime = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_SHUTTERSPEED: // More complicated way of expressing exposure time, so only use // this value if we don't already have it from somewhere else. if (ImageInfo.ExposureTime == 0){ ImageInfo.ExposureTime = (float)(1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2))); } break; case TAG_FLASH: if (ConvertAnyFormat(ValuePtr, Format)){ ImageInfo.FlashUsed = 1; } break; case TAG_EXIF_IMAGELENGTH: case TAG_EXIF_IMAGEWIDTH: // Use largest of height and width to deal with images that have been // rotated to portrait format. a = (int)ConvertAnyFormat(ValuePtr, Format); if (ExifImageWidth < a) ExifImageWidth = a; break; case TAG_FOCALPLANEXRES: FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format); break; case TAG_FOCALPLANEUNITS: switch((int)ConvertAnyFormat(ValuePtr, Format)){ case 1: FocalplaneUnits = 25.4; break; // inch case 2: // According to the information I was using, 2 means meters. // But looking at the Cannon powershot's files, inches is the only // sensible value. FocalplaneUnits = 25.4; break; case 3: FocalplaneUnits = 10; break; // centimeter case 4: FocalplaneUnits = 1; break; // milimeter case 5: FocalplaneUnits = .001; break; // micrometer } break; // Remaining cases contributed by: Volker C. Schoech ([email protected]) case TAG_EXPOSURE_BIAS: ImageInfo.ExposureBias = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_WHITEBALANCE: ImageInfo.Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_METERING_MODE: ImageInfo.MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_EXPOSURE_PROGRAM: ImageInfo.ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_ISO_EQUIVALENT: ImageInfo.ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format); if ( ImageInfo.ISOequivalent < 80 ) ImageInfo.ISOequivalent *= 200; break; case TAG_COMPRESSION_LEVEL: ImageInfo.CompressionLevel = (int)ConvertAnyFormat(ValuePtr, Format); break; } if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET){ char * SubdirStart; SubdirStart = OffsetBase + Get32u(ValuePtr); if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){ ErrExit("Illegal subdirectory link"); } ProcessExifDir(SubdirStart, OffsetBase, ExifLength); continue; } } }