www.pudn.com > CXIMAGE_SRC.ZIP > ximaexif.cpp


/* 
 * File:	ximaexif.cpp 
 * Purpose:	Platform Independent JBG Image Class Loader and Writer 
 * 18/Aug/2002  
 * CxImage version 5.00 23/Aug/2002 
 */ 
 
#include "ximajpg.h" 
 
#if CXIMAGEJPG_SUPPORT_EXIF 
 
//////////////////////////////////////////////////////////////////////////////// 
CxImageJPG::CxExifInfo::CxExifInfo(EXIFINFO* info) 
{ 
	m_exifinfo = info; 
	memset(m_exifinfo, 0, sizeof(EXIFINFO)); 
	m_szLastError[0]='\0'; 
} 
//////////////////////////////////////////////////////////////////////////////// 
bool CxImageJPG::CxExifInfo::DecodeInfo(struct jpeg_decompress_struct cinfo) 
{ 
    struct jpeg_marker_struct * markerP; 
    bool   found_one=false; 
 
    for (markerP = cinfo.marker_list; markerP; markerP = markerP->next) { 
        if (is_exif(*markerP)) { 
 
			if (!exif_process(markerP->data, markerP->data_length, m_exifinfo)) return false; 
 
            found_one = true; 
        } 
	} 
 
    if (!found_one){ 
		strcpy(m_szLastError,"No EXIF info in image."); 
		return false; 
	} 
 
	return true; 
} 
//////////////////////////////////////////////////////////////////////////////// 
/*-------------------------------------------------------------------------- 
   Process a EXIF marker 
   Describes all the drivel that most digital cameras include... 
--------------------------------------------------------------------------*/ 
bool CxImageJPG::CxExifInfo::exif_process(unsigned char * CharBuf, unsigned int length, EXIFINFO* pInfo) 
{ 
    int ExifSettingsLength; 
    unsigned char * LastExifRefd; 
 
    pInfo->FlashUsed = 0;  
    /* If it's from a digicam, and it used flash, it says so. */ 
    pInfo->Comments[0] = '\0';  /* Initial value - null string */ 
 
    FocalplaneXRes = 0; 
    FocalplaneUnits = 0; 
    ExifImageWidth = 0; 
 
 
    {   /* Check the EXIF header component */ 
        static const unsigned char ExifHeader[] = "Exif\0\0"; 
        if (memcmp(CharBuf+0, ExifHeader,6)){ 
			strcpy(m_szLastError,"Incorrect Exif header"); 
			return false; 
		} 
    } 
 
    if (memcmp(CharBuf+6,"II",2) == 0){ 
        MotorolaOrder = 0; 
    }else{ 
        if (memcmp(CharBuf+6,"MM",2) == 0){ 
            MotorolaOrder = 1; 
        }else{ 
            strcpy(m_szLastError,"Invalid Exif alignment marker."); 
			return false; 
        } 
    } 
 
    /* Check the next two values for correctness. */ 
    if (Get16u(CharBuf+8) != 0x2a || Get32u(CharBuf+10) != 0x08){ 
        strcpy(m_szLastError,"Invalid Exif start (1)"); 
		return false; 
    } 
 
    LastExifRefd = CharBuf; 
 
    /* First directory starts 16 bytes in.  Offsets start at 8 bytes in. */ 
    if (!ProcessExifDir(CharBuf+14, CharBuf+6, length-6, pInfo, &LastExifRefd)) 
		return false; 
 
    /* This is how far the interesting (non thumbnail) part of the exif went. 
     */ 
    ExifSettingsLength = LastExifRefd - CharBuf; 
 
    /* Compute the CCD width, in milimeters. */ 
    if (FocalplaneXRes != 0){ 
        pInfo->CCDWidth =  
            (float)(ExifImageWidth * FocalplaneUnits / FocalplaneXRes); 
    } 
 
	return true; 
} 
//////////////////////////////////////////////////////////////////////////////// 
/*-------------------------------------------------------------------------- 
   Convert a 16 bit unsigned value from file's native byte order 
--------------------------------------------------------------------------*/ 
int CxImageJPG::CxExifInfo::Get16u(void * Short) 
{ 
    if (MotorolaOrder){ 
        return (((unsigned char *)Short)[0] << 8) | ((unsigned char *)Short)[1]; 
    }else{ 
        return (((unsigned char *)Short)[1] << 8) | ((unsigned char *)Short)[0]; 
    } 
} 
//////////////////////////////////////////////////////////////////////////////// 
/*-------------------------------------------------------------------------- 
   Convert a 32 bit signed value from file's native byte order 
--------------------------------------------------------------------------*/ 
int CxImageJPG::CxExifInfo::Get32s(void * Long) 
{ 
    if (MotorolaOrder){ 
        return  ((( char *)Long)[0] << 24) | (((unsigned char *)Long)[1] << 16) 
              | (((unsigned char *)Long)[2] << 8 ) | (((unsigned char *)Long)[3] << 0 ); 
    }else{ 
        return  ((( char *)Long)[3] << 24) | (((unsigned char *)Long)[2] << 16) 
              | (((unsigned char *)Long)[1] << 8 ) | (((unsigned char *)Long)[0] << 0 ); 
    } 
} 
//////////////////////////////////////////////////////////////////////////////// 
/*-------------------------------------------------------------------------- 
   Convert a 32 bit unsigned value from file's native byte order 
--------------------------------------------------------------------------*/ 
unsigned long CxImageJPG::CxExifInfo::Get32u(void * Long) 
{ 
    return (unsigned long)Get32s(Long) & 0xffffffff; 
} 
//////////////////////////////////////////////////////////////////////////////// 
 
/* Describes format descriptor */ 
static const int BytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8}; 
#define NUM_FORMATS 12 
 
#define FMT_BYTE       1  
#define FMT_STRING     2 
#define FMT_USHORT     3 
#define FMT_ULONG      4 
#define FMT_URATIONAL  5 
#define FMT_SBYTE      6 
#define FMT_UNDEFINED  7 
#define FMT_SSHORT     8 
#define FMT_SLONG      9 
#define FMT_SRATIONAL 10 
#define FMT_SINGLE    11 
#define FMT_DOUBLE    12 
 
/* Describes tag values */ 
 
#define TAG_EXIF_OFFSET       0x8769 
#define TAG_INTEROP_OFFSET    0xa005 
 
#define TAG_MAKE              0x010F 
#define TAG_MODEL             0x0110 
 
#define TAG_EXPOSURETIME      0x829A 
#define TAG_FNUMBER           0x829D 
 
#define TAG_SHUTTERSPEED      0x9201 
#define TAG_APERTURE          0x9202 
#define TAG_MAXAPERTURE       0x9205 
#define TAG_FOCALLENGTH       0x920A 
 
#define TAG_DATETIME_ORIGINAL 0x9003 
#define TAG_USERCOMMENT       0x9286 
 
#define TAG_SUBJECT_DISTANCE  0x9206 
#define TAG_FLASH             0x9209 
 
#define TAG_FOCALPLANEXRES    0xa20E 
#define TAG_FOCALPLANEUNITS   0xa210 
#define TAG_EXIF_IMAGEWIDTH   0xA002 
#define TAG_EXIF_IMAGELENGTH  0xA003 
 
/* the following is added 05-jan-2001 vcs */ 
#define TAG_EXPOSURE_BIAS     0x9204 
#define TAG_WHITEBALANCE      0x9208 
#define TAG_METERING_MODE     0x9207 
#define TAG_EXPOSURE_PROGRAM  0x8822 
#define TAG_ISO_EQUIVALENT    0x8827 
#define TAG_COMPRESSION_LEVEL 0x9102 
 
#define TAG_THUMBNAIL_OFFSET  0x0201 
#define TAG_THUMBNAIL_LENGTH  0x0202 
 
 
/*-------------------------------------------------------------------------- 
   Process one of the nested EXIF directories. 
--------------------------------------------------------------------------*/ 
bool CxImageJPG::CxExifInfo::ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, unsigned ExifLength, 
                           EXIFINFO * const pInfo, unsigned char ** const LastExifRefdP ) 
{ 
    int de; 
    int a; 
    int NumDirEntries; 
    unsigned ThumbnailOffset = 0; 
    unsigned ThumbnailSize = 0; 
 
    NumDirEntries = Get16u(DirStart); 
 
    if ((DirStart+2+NumDirEntries*12) > (OffsetBase+ExifLength)){ 
        strcpy(m_szLastError,"Illegally sized directory"); 
		return false; 
    } 
 
    for (de=0;de= NUM_FORMATS) { 
            /* (-1) catches illegal zero case as unsigned underflows 
               to positive large.   
            */ 
            strcpy(m_szLastError,"Illegal format code in EXIF dir"); 
			return false; 
		} 
 
        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 */ 
                strcpy(m_szLastError,"Illegal pointer offset value in EXIF."); 
				return false; 
            } 
            ValuePtr = OffsetBase+OffsetVal; 
        }else{ 
            /* 4 bytes or less and value is in the dir entry itself */ 
            ValuePtr = DirEntry+8; 
        } 
 
        if (*LastExifRefdP < 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. 
            */ 
            *LastExifRefdP = ValuePtr+ByteCount; 
        } 
 
        /* Extract useful components of tag */ 
        switch(Tag){ 
 
            case TAG_MAKE: 
                strncpy(pInfo->CameraMake, (char*)ValuePtr, 31); 
                break; 
 
            case TAG_MODEL: 
                strncpy(pInfo->CameraModel, (char*)ValuePtr, 39); 
                break; 
 
            case TAG_DATETIME_ORIGINAL: 
                strncpy(pInfo->DateTime, (char*)ValuePtr, 19); 
                break; 
 
            case TAG_USERCOMMENT: 
                /* Olympus has this padded with trailing spaces. 
                   Remove these first.  
                */ 
                for (a=ByteCount;;){ 
                    a--; 
                    if (((char*)ValuePtr)[a] == ' '){ 
                        ((char*)ValuePtr)[a] = '\0'; 
                    }else{ 
                        break; 
                    } 
                    if (a == 0) break; 
                } 
 
                /* Copy the comment */ 
                if (memcmp(ValuePtr, "ASCII",5) == 0){ 
                    for (a=5;a<10;a++){ 
                        char c; 
                        c = ((char*)ValuePtr)[a]; 
                        if (c != '\0' && c != ' '){ 
                            strncpy(pInfo->Comments, (char*)ValuePtr+a,  
                                    199); 
                            break; 
                        } 
                    } 
                     
                }else{ 
                    strncpy(pInfo->Comments, (char*)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) 
                   */ 
                pInfo->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 (pInfo->ApertureFNumber == 0){ 
                    pInfo->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.  
                */ 
 
                pInfo->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. 
                */ 
                pInfo->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)  
                */ 
                pInfo->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 (pInfo->ExposureTime == 0){ 
                    pInfo->ExposureTime = (float) 
                        (1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2))); 
                } 
                break; 
 
            case TAG_FLASH: 
                if (ConvertAnyFormat(ValuePtr, Format)){ 
                    pInfo->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; /* 1 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;  /* 1 centimeter*/ 
                    case 4: FocalplaneUnits = 1;    break;  /* 1 millimeter*/ 
                    case 5: FocalplaneUnits = .001; break;  /* 1 micrometer*/ 
                } 
                break; 
 
                /* Remaining cases contributed by: Volker C. Schoech 
                   (schoech@gmx.de) 
                */ 
 
            case TAG_EXPOSURE_BIAS: 
                pInfo->ExposureBias =  
                    (float) ConvertAnyFormat(ValuePtr, Format); 
                break; 
 
            case TAG_WHITEBALANCE: 
                pInfo->Whitebalance =  
                    (int)ConvertAnyFormat(ValuePtr, Format); 
                break; 
 
            case TAG_METERING_MODE: 
                pInfo->MeteringMode =  
                    (int)ConvertAnyFormat(ValuePtr, Format); 
                break; 
 
            case TAG_EXPOSURE_PROGRAM: 
                pInfo->ExposureProgram =  
                    (int)ConvertAnyFormat(ValuePtr, Format); 
                break; 
 
            case TAG_ISO_EQUIVALENT: 
                pInfo->ISOequivalent =  
                    (int)ConvertAnyFormat(ValuePtr, Format); 
                if ( pInfo->ISOequivalent < 50 )  
                    pInfo->ISOequivalent *= 200; 
                break; 
 
            case TAG_COMPRESSION_LEVEL: 
                pInfo->CompressionLevel =  
                    (int)ConvertAnyFormat(ValuePtr, Format); 
                break; 
 
            case TAG_THUMBNAIL_OFFSET: 
                ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format); 
                break; 
 
            case TAG_THUMBNAIL_LENGTH: 
                ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format); 
                break; 
 
        } 
 
        if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET){ 
            unsigned char * SubdirStart; 
            SubdirStart = OffsetBase + Get32u(ValuePtr); 
            if (SubdirStart < OffsetBase ||  
                SubdirStart > OffsetBase+ExifLength){ 
                strcpy(m_szLastError,"Illegal subdirectory link"); 
				return false; 
            } 
            ProcessExifDir(SubdirStart, OffsetBase, ExifLength, pInfo, LastExifRefdP); 
            continue; 
        } 
    } 
 
 
    { 
        /* In addition to linking to subdirectories via exif tags, 
           there's also a potential link to another directory at the end 
           of each directory.  This has got to be the result of a 
           committee!   
        */ 
        unsigned char * SubdirStart; 
        unsigned Offset; 
        Offset = Get16u(DirStart+2+12*NumDirEntries); 
        if (Offset){ 
            SubdirStart = OffsetBase + Offset; 
            if (SubdirStart < OffsetBase  
                || SubdirStart > OffsetBase+ExifLength){ 
                strcpy(m_szLastError,"Illegal subdirectory link"); 
				return false; 
            } 
            ProcessExifDir(SubdirStart, OffsetBase, ExifLength, pInfo, LastExifRefdP); 
        } 
    } 
 
 
    if (ThumbnailSize && ThumbnailOffset){ 
        if (ThumbnailSize + ThumbnailOffset <= ExifLength){ 
            /* The thumbnail pointer appears to be valid.  Store it. */ 
            pInfo->ThumbnailPointer = OffsetBase + ThumbnailOffset; 
            pInfo->ThumbnailSize = ThumbnailSize; 
        } 
    } 
 
	return true; 
} 
//////////////////////////////////////////////////////////////////////////////// 
/*-------------------------------------------------------------------------- 
   Evaluate number, be it int, rational, or float from directory. 
--------------------------------------------------------------------------*/ 
double CxImageJPG::CxExifInfo::ConvertAnyFormat(void * ValuePtr, int Format) 
{ 
    double Value; 
    Value = 0; 
 
    switch(Format){ 
        case FMT_SBYTE:     Value = *(signed char *)ValuePtr;  break; 
        case FMT_BYTE:      Value = *(unsigned char *)ValuePtr;        break; 
 
        case FMT_USHORT:    Value = Get16u(ValuePtr);          break; 
        case FMT_ULONG:     Value = Get32u(ValuePtr);          break; 
 
        case FMT_URATIONAL: 
        case FMT_SRATIONAL:  
            { 
                int Num,Den; 
                Num = Get32s(ValuePtr); 
                Den = Get32s(4+(char *)ValuePtr); 
                if (Den == 0){ 
                    Value = 0; 
                }else{ 
                    Value = (double)Num/Den; 
                } 
                break; 
            } 
 
        case FMT_SSHORT:    Value = (signed short)Get16u(ValuePtr);  break; 
        case FMT_SLONG:     Value = Get32s(ValuePtr);                break; 
 
        /* Not sure if this is correct (never seen float used in Exif format) 
         */ 
        case FMT_SINGLE:    Value = (double)*(float *)ValuePtr;      break; 
        case FMT_DOUBLE:    Value = *(double *)ValuePtr;             break; 
    } 
    return Value; 
} 
//////////////////////////////////////////////////////////////////////////////// 
/*---------------------------------------------------------------------------- 
   Return true iff the JPEG miscellaneous marker 'marker' is an Exif  
   header. 
-----------------------------------------------------------------------------*/ 
bool CxImageJPG::CxExifInfo::is_exif(struct jpeg_marker_struct const marker) 
{ 
    bool retval; 
     
    if (marker.marker == JPEG_APP0+1) { 
        if (memcmp(marker.data, "Exif", 4) == 0) 
            retval = true; 
        else retval = false; 
    } 
    else retval = false; 
 
    return retval; 
} 
//////////////////////////////////////////////////////////////////////////////// 
#endif 	// CXIMAGEJPG_SUPPORT_EXIF