X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;f=utilities%2FCxImage%2Fximaexif.cpp;h=d36e20c2c53994eb020f575be0c5f24c12049eb2;hb=refs%2Fheads%2FGammaIndex3D;hp=8d9404b8ad67fe052509e2d2412be68aa9cec3e1;hpb=19d5db17f1c0e98cf84a6cb83643404a550a12a4;p=clitk.git diff --git a/utilities/CxImage/ximaexif.cpp b/utilities/CxImage/ximaexif.cpp index 8d9404b..d36e20c 100644 --- a/utilities/CxImage/ximaexif.cpp +++ b/utilities/CxImage/ximaexif.cpp @@ -1,878 +1,878 @@ -/* - * File: ximaexif.cpp - * Purpose: EXIF reader - * 18/Aug/2002 Davide Pizzolato - www.xdp.it - * CxImage version 6.0.0 02/Feb/2008 - * based on jhead-1.8 by Matthias Wandel - */ - -#include "ximajpg.h" - -#if CXIMAGEJPG_SUPPORT_EXIF - -//////////////////////////////////////////////////////////////////////////////// -CxImageJPG::CxExifInfo::CxExifInfo(EXIFINFO* info) -{ - if (info) { - m_exifinfo = info; - freeinfo = false; - } else { - m_exifinfo = new EXIFINFO; - memset(m_exifinfo,0,sizeof(EXIFINFO)); - freeinfo = true; - } - - m_szLastError[0]='\0'; - ExifImageWidth = MotorolaOrder = 0; - SectionsRead=0; - memset(&Sections, 0, MAX_SECTIONS * sizeof(Section_t)); -} -//////////////////////////////////////////////////////////////////////////////// -CxImageJPG::CxExifInfo::~CxExifInfo() -{ - for(int i=0;iGetC(); - - if (a != 0xff || hFile->GetC() != M_SOI){ - return FALSE; - } - - for(;;){ - int itemlen; - int marker = 0; - int ll,lh, got; - BYTE * Data; - - if (SectionsRead >= MAX_SECTIONS){ - strcpy(m_szLastError,"Too many sections in jpg file"); - return false; - } - - for (a=0;a<7;a++){ - marker = hFile->GetC(); - if (marker != 0xff) break; - - if (a >= 6){ - printf("too many padding bytes\n"); - return false; - } - } - - if (marker == 0xff){ - // 0xff is legal padding, but if we get that many, something's wrong. - strcpy(m_szLastError,"too many padding bytes!"); - return false; - } - - Sections[SectionsRead].Type = marker; - - // Read the length of the section. - lh = hFile->GetC(); - ll = hFile->GetC(); - - itemlen = (lh << 8) | ll; - - if (itemlen < 2){ - strcpy(m_szLastError,"invalid marker"); - return false; - } - - Sections[SectionsRead].Size = itemlen; - - Data = (BYTE *)malloc(itemlen); - if (Data == NULL){ - strcpy(m_szLastError,"Could not allocate memory"); - return false; - } - Sections[SectionsRead].Data = Data; - - // Store first two pre-read bytes. - Data[0] = (BYTE)lh; - Data[1] = (BYTE)ll; - - got = hFile->Read(Data+2, 1, itemlen-2); // Read the whole section. - if (got != itemlen-2){ - strcpy(m_szLastError,"Premature end of file?"); - return false; - } - SectionsRead += 1; - - switch(marker){ - - case M_SOS: // stop before hitting compressed data - // If reading entire image is requested, read the rest of the data. - if (nReadMode & EXIF_READ_IMAGE){ - int cp, ep, size; - // Determine how much file is left. - cp = hFile->Tell(); - hFile->Seek(0, SEEK_END); - ep = hFile->Tell(); - hFile->Seek(cp, SEEK_SET); - - size = ep-cp; - Data = (BYTE *)malloc(size); - if (Data == NULL){ - strcpy(m_szLastError,"could not allocate data for entire image"); - return false; - } - - got = hFile->Read(Data, 1, size); - if (got != size){ - strcpy(m_szLastError,"could not read the rest of the image"); - return false; - } - - Sections[SectionsRead].Data = Data; - Sections[SectionsRead].Size = size; - Sections[SectionsRead].Type = PSEUDO_IMAGE_MARKER; - SectionsRead ++; - } - return true; - - case M_EOI: // in case it's a tables-only JPEG stream - printf("No image in jpeg!\n"); - return FALSE; - - case M_COM: // Comment section - if (HaveCom || ((nReadMode & EXIF_READ_EXIF) == 0)){ - // Discard this section. - free(Sections[--SectionsRead].Data); - Sections[SectionsRead].Data=0; - }else{ - process_COM(Data, itemlen); - HaveCom = TRUE; - } - break; - - case M_JFIF: - // Regular jpegs always have this tag, exif images have the exif - // marker instead, althogh ACDsee will write images with both markers. - // this program will re-create this marker on absence of exif marker. - // hence no need to keep the copy from the file. - free(Sections[--SectionsRead].Data); - Sections[SectionsRead].Data=0; - break; - - case M_EXIF: - // Seen files from some 'U-lead' software with Vivitar scanner - // that uses marker 31 for non exif stuff. Thus make sure - // it says 'Exif' in the section before treating it as exif. - if ((nReadMode & EXIF_READ_EXIF) && memcmp(Data+2, "Exif", 4) == 0){ - m_exifinfo->IsExif = process_EXIF((BYTE *)Data+2, itemlen); - }else{ - // Discard this section. - free(Sections[--SectionsRead].Data); - Sections[SectionsRead].Data=0; - } - break; - - case M_SOF0: - case M_SOF1: - case M_SOF2: - case M_SOF3: - case M_SOF5: - case M_SOF6: - case M_SOF7: - case M_SOF9: - case M_SOF10: - case M_SOF11: - case M_SOF13: - case M_SOF14: - case M_SOF15: - process_SOFn(Data, marker); - break; - default: - // Skip any other sections. - //if (ShowTags) printf("Jpeg section marker 0x%02x size %d\n",marker, itemlen); - break; - } - } - return true; -} -//////////////////////////////////////////////////////////////////////////////// -/*-------------------------------------------------------------------------- - Process a EXIF marker - Describes all the drivel that most digital cameras include... ---------------------------------------------------------------------------*/ -bool CxImageJPG::CxExifInfo::process_EXIF(unsigned char * CharBuf, unsigned int length) -{ - m_exifinfo->FlashUsed = 0; - /* If it's from a digicam, and it used flash, it says so. */ - m_exifinfo->Comments[0] = '\0'; /* Initial value - null string */ - - 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){ - strcpy(m_szLastError,"Invalid Exif start (1)"); - return false; - } - - int FirstOffset = Get32u(CharBuf+10); - /* - if (FirstOffset < 8 || FirstOffset > 16){ - // I used to ensure this was set to 8 (website I used indicated its 8) - // but PENTAX Optio 230 has it set differently, and uses it as offset. (Sept 11 2002) - strcpy(m_szLastError,"Suspicious offset of first IFD value"); - return false; - }*/ - - unsigned char * LastExifRefd = CharBuf; - - /* First directory starts 16 bytes in. Offsets start at 8 bytes in. */ - if (!ProcessExifDir(CharBuf+14, CharBuf+6, length-6, m_exifinfo, &LastExifRefd)) - return false; - - /* give a chance for a second directory */ - if (FirstOffset > 8) { - if (!ProcessExifDir(CharBuf+14+FirstOffset-8, CharBuf+6, length-6, m_exifinfo, &LastExifRefd)) - return false; - } - - /* This is how far the interesting (non thumbnail) part of the exif went. */ - // int ExifSettingsLength = LastExifRefd - CharBuf; - - /* Compute the CCD width, in milimeters. */ - if (m_exifinfo->FocalplaneXRes != 0){ - m_exifinfo->CCDWidth = (float)(ExifImageWidth * m_exifinfo->FocalplaneUnits / m_exifinfo->FocalplaneXRes); - } - - return true; -} -//-------------------------------------------------------------------------- -// Get 16 bits motorola order (always) for jpeg header stuff. -//-------------------------------------------------------------------------- -int CxImageJPG::CxExifInfo::Get16m(void * Short) -{ - return (((unsigned char *)Short)[0] << 8) | ((unsigned char *)Short)[1]; -} -//////////////////////////////////////////////////////////////////////////////// -/*-------------------------------------------------------------------------- - 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 ---------------------------------------------------------------------------*/ -long 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_VERSION 0x9000 -#define TAG_EXIF_OFFSET 0x8769 -#define TAG_INTEROP_OFFSET 0xa005 - -#define TAG_MAKE 0x010F -#define TAG_MODEL 0x0110 - -#define TAG_ORIENTATION 0x0112 -#define TAG_XRESOLUTION 0x011A -#define TAG_YRESOLUTION 0x011B -#define TAG_RESOLUTIONUNIT 0x0128 - -#define TAG_EXPOSURETIME 0x829A -#define TAG_FNUMBER 0x829D - -#define TAG_SHUTTERSPEED 0x9201 -#define TAG_APERTURE 0x9202 -#define TAG_BRIGHTNESS 0x9203 -#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_FOCALPLANEYRES 0xa20F -#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 m_exifinfo, unsigned char ** const LastExifRefdP, int NestingLevel) -{ - int de; - int a; - int NumDirEntries; - unsigned ThumbnailOffset = 0; - unsigned ThumbnailSize = 0; - - if (NestingLevel > 4){ - strcpy(m_szLastError,"Maximum directory nesting exceeded (corrupt exif header)"); - return false; - } - - 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(m_exifinfo->CameraMake, (char*)ValuePtr, 31); - break; - - case TAG_MODEL: - strncpy(m_exifinfo->CameraModel, (char*)ValuePtr, 39); - break; - - case TAG_EXIF_VERSION: - strncpy(m_exifinfo->Version,(char*)ValuePtr, 4); - break; - - case TAG_DATETIME_ORIGINAL: - strncpy(m_exifinfo->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(m_exifinfo->Comments, (char*)ValuePtr+a, 199); - break; - } - } - - }else{ - strncpy(m_exifinfo->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) - */ - m_exifinfo->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 (m_exifinfo->ApertureFNumber == 0){ - m_exifinfo->ApertureFNumber = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0f)*0.5); - } - break; - - case TAG_BRIGHTNESS: - m_exifinfo->Brightness = (float)ConvertAnyFormat(ValuePtr, Format); - break; - - case TAG_FOCALLENGTH: - /* Nice digital cameras actually save the focal length - as a function of how farthey are zoomed in. - */ - - m_exifinfo->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. - */ - m_exifinfo->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) - */ - m_exifinfo->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 (m_exifinfo->ExposureTime == 0){ - m_exifinfo->ExposureTime = (float) - (1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0f))); - } - break; - - case TAG_FLASH: - if ((int)ConvertAnyFormat(ValuePtr, Format) & 7){ - m_exifinfo->FlashUsed = 1; - }else{ - m_exifinfo->FlashUsed = 0; - } - break; - - case TAG_ORIENTATION: - m_exifinfo->Orientation = (int)ConvertAnyFormat(ValuePtr, Format); - if (m_exifinfo->Orientation < 1 || m_exifinfo->Orientation > 8){ - strcpy(m_szLastError,"Undefined rotation value"); - m_exifinfo->Orientation = 0; - } - 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: - m_exifinfo->FocalplaneXRes = (float)ConvertAnyFormat(ValuePtr, Format); - break; - - case TAG_FOCALPLANEYRES: - m_exifinfo->FocalplaneYRes = (float)ConvertAnyFormat(ValuePtr, Format); - break; - - case TAG_RESOLUTIONUNIT: - switch((int)ConvertAnyFormat(ValuePtr, Format)){ - case 1: m_exifinfo->ResolutionUnit = 1.0f; break; /* 1 inch */ - case 2: m_exifinfo->ResolutionUnit = 1.0f; break; - case 3: m_exifinfo->ResolutionUnit = 0.3937007874f; break; /* 1 centimeter*/ - case 4: m_exifinfo->ResolutionUnit = 0.03937007874f; break; /* 1 millimeter*/ - case 5: m_exifinfo->ResolutionUnit = 0.00003937007874f; /* 1 micrometer*/ - } - break; - - case TAG_FOCALPLANEUNITS: - switch((int)ConvertAnyFormat(ValuePtr, Format)){ - case 1: m_exifinfo->FocalplaneUnits = 1.0f; break; /* 1 inch */ - case 2: m_exifinfo->FocalplaneUnits = 1.0f; break; - case 3: m_exifinfo->FocalplaneUnits = 0.3937007874f; break; /* 1 centimeter*/ - case 4: m_exifinfo->FocalplaneUnits = 0.03937007874f; break; /* 1 millimeter*/ - case 5: m_exifinfo->FocalplaneUnits = 0.00003937007874f; /* 1 micrometer*/ - } - break; - - // Remaining cases contributed by: Volker C. Schoech - - case TAG_EXPOSURE_BIAS: - m_exifinfo->ExposureBias = (float) ConvertAnyFormat(ValuePtr, Format); - break; - - case TAG_WHITEBALANCE: - m_exifinfo->Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format); - break; - - case TAG_METERING_MODE: - m_exifinfo->MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format); - break; - - case TAG_EXPOSURE_PROGRAM: - m_exifinfo->ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format); - break; - - case TAG_ISO_EQUIVALENT: - m_exifinfo->ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format); - if ( m_exifinfo->ISOequivalent < 50 ) m_exifinfo->ISOequivalent *= 200; - break; - - case TAG_COMPRESSION_LEVEL: - m_exifinfo->CompressionLevel = (int)ConvertAnyFormat(ValuePtr, Format); - break; - - case TAG_XRESOLUTION: - m_exifinfo->Xresolution = (float)ConvertAnyFormat(ValuePtr, Format); - break; - case TAG_YRESOLUTION: - m_exifinfo->Yresolution = (float)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; - unsigned Offset = Get32u(ValuePtr); - if (Offset>8){ - SubdirStart = OffsetBase + Offset; - if (SubdirStart < OffsetBase || - SubdirStart > OffsetBase+ExifLength){ - strcpy(m_szLastError,"Illegal subdirectory link"); - return false; - } - ProcessExifDir(SubdirStart, OffsetBase, ExifLength, m_exifinfo, LastExifRefdP, NestingLevel+1); - } - 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, m_exifinfo, LastExifRefdP, NestingLevel+1); - } - } - - - if (ThumbnailSize && ThumbnailOffset){ - if (ThumbnailSize + ThumbnailOffset <= ExifLength){ - /* The thumbnail pointer appears to be valid. Store it. */ - m_exifinfo->ThumbnailPointer = OffsetBase + ThumbnailOffset; - m_exifinfo->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; -} -//////////////////////////////////////////////////////////////////////////////// -void CxImageJPG::CxExifInfo::process_COM (const BYTE * Data, int length) -{ - int ch; - char Comment[MAX_COMMENT+1]; - int nch; - int a; - - nch = 0; - - if (length > MAX_COMMENT) length = MAX_COMMENT; // Truncate if it won't fit in our structure. - - for (a=2;aComments,Comment); -} -//////////////////////////////////////////////////////////////////////////////// -void CxImageJPG::CxExifInfo::process_SOFn (const BYTE * Data, int marker) -{ - int data_precision, num_components; - - data_precision = Data[2]; - m_exifinfo->Height = Get16m((void*)(Data+3)); - m_exifinfo->Width = Get16m((void*)(Data+5)); - num_components = Data[7]; - - if (num_components == 3){ - m_exifinfo->IsColor = 1; - }else{ - m_exifinfo->IsColor = 0; - } - - m_exifinfo->Process = marker; - - //if (ShowTags) printf("JPEG image is %uw * %uh, %d color components, %d bits per sample\n", - // ImageInfo.Width, ImageInfo.Height, num_components, data_precision); -} -//////////////////////////////////////////////////////////////////////////////// -/** - * this will work only on a CxImageJPG object, if the image originally has valid EXIF data - \verbatim - CxImageJPG jpg; - CxIOFile in,out; - in.Open("D:\\exif_in.jpg","rb"); - out.Open("D:\\exif_out.jpg","w+b"); - jpg.Decode(&in); - if (jpg.IsValid()){ - jpg.RotateLeft(); - jpg.Encode(&out); - } - \endverbatim -*/ -bool CxImageJPG::CxExifInfo::EncodeExif(CxFile * hFile) -{ - int a; - - if (FindSection(M_SOS)==NULL){ - strcpy(m_szLastError,"Can't write exif : didn't read all"); - return false; - } - - // Initial static jpeg marker. - hFile->PutC(0xff); - hFile->PutC(0xd8); - - if (Sections[0].Type != M_EXIF && Sections[0].Type != M_JFIF){ - // The image must start with an exif or jfif marker. If we threw those away, create one. - static BYTE JfifHead[18] = { - 0xff, M_JFIF, - 0x00, 0x10, 'J' , 'F' , 'I' , 'F' , 0x00, 0x01, - 0x01, 0x01, 0x01, 0x2C, 0x01, 0x2C, 0x00, 0x00 - }; - hFile->Write(JfifHead, 18, 1); - } - - // Write all the misc sections - for (a=0;aPutC(0xff); - hFile->PutC((unsigned char)(Sections[a].Type)); - hFile->Write(Sections[a].Data, Sections[a].Size, 1); - } - - // Write the remaining image data. - hFile->Write(Sections[a].Data, Sections[a].Size, 1); - - return true; -} -//////////////////////////////////////////////////////////////////////////////// -void CxImageJPG::CxExifInfo::DiscardAllButExif() -{ - Section_t ExifKeeper; - Section_t CommentKeeper; - int a; - - memset(&ExifKeeper, 0, sizeof(ExifKeeper)); - memset(&CommentKeeper, 0, sizeof(ExifKeeper)); - - for (a=0;a + */ + +#include "ximajpg.h" + +#if CXIMAGEJPG_SUPPORT_EXIF + +//////////////////////////////////////////////////////////////////////////////// +CxImageJPG::CxExifInfo::CxExifInfo(EXIFINFO* info) +{ + if (info) { + m_exifinfo = info; + freeinfo = false; + } else { + m_exifinfo = new EXIFINFO; + memset(m_exifinfo,0,sizeof(EXIFINFO)); + freeinfo = true; + } + + m_szLastError[0]='\0'; + ExifImageWidth = MotorolaOrder = 0; + SectionsRead=0; + memset(&Sections, 0, MAX_SECTIONS * sizeof(Section_t)); +} +//////////////////////////////////////////////////////////////////////////////// +CxImageJPG::CxExifInfo::~CxExifInfo() +{ + for(int i=0;iGetC(); + + if (a != 0xff || hFile->GetC() != M_SOI){ + return FALSE; + } + + for(;;){ + int itemlen; + int marker = 0; + int ll,lh, got; + BYTE * Data; + + if (SectionsRead >= MAX_SECTIONS){ + strcpy(m_szLastError,"Too many sections in jpg file"); + return false; + } + + for (a=0;a<7;a++){ + marker = hFile->GetC(); + if (marker != 0xff) break; + + if (a >= 6){ + printf("too many padding bytes\n"); + return false; + } + } + + if (marker == 0xff){ + // 0xff is legal padding, but if we get that many, something's wrong. + strcpy(m_szLastError,"too many padding bytes!"); + return false; + } + + Sections[SectionsRead].Type = marker; + + // Read the length of the section. + lh = hFile->GetC(); + ll = hFile->GetC(); + + itemlen = (lh << 8) | ll; + + if (itemlen < 2){ + strcpy(m_szLastError,"invalid marker"); + return false; + } + + Sections[SectionsRead].Size = itemlen; + + Data = (BYTE *)malloc(itemlen); + if (Data == NULL){ + strcpy(m_szLastError,"Could not allocate memory"); + return false; + } + Sections[SectionsRead].Data = Data; + + // Store first two pre-read bytes. + Data[0] = (BYTE)lh; + Data[1] = (BYTE)ll; + + got = hFile->Read(Data+2, 1, itemlen-2); // Read the whole section. + if (got != itemlen-2){ + strcpy(m_szLastError,"Premature end of file?"); + return false; + } + SectionsRead += 1; + + switch(marker){ + + case M_SOS: // stop before hitting compressed data + // If reading entire image is requested, read the rest of the data. + if (nReadMode & EXIF_READ_IMAGE){ + int cp, ep, size; + // Determine how much file is left. + cp = hFile->Tell(); + hFile->Seek(0, SEEK_END); + ep = hFile->Tell(); + hFile->Seek(cp, SEEK_SET); + + size = ep-cp; + Data = (BYTE *)malloc(size); + if (Data == NULL){ + strcpy(m_szLastError,"could not allocate data for entire image"); + return false; + } + + got = hFile->Read(Data, 1, size); + if (got != size){ + strcpy(m_szLastError,"could not read the rest of the image"); + return false; + } + + Sections[SectionsRead].Data = Data; + Sections[SectionsRead].Size = size; + Sections[SectionsRead].Type = PSEUDO_IMAGE_MARKER; + SectionsRead ++; + } + return true; + + case M_EOI: // in case it's a tables-only JPEG stream + printf("No image in jpeg!\n"); + return FALSE; + + case M_COM: // Comment section + if (HaveCom || ((nReadMode & EXIF_READ_EXIF) == 0)){ + // Discard this section. + free(Sections[--SectionsRead].Data); + Sections[SectionsRead].Data=0; + }else{ + process_COM(Data, itemlen); + HaveCom = TRUE; + } + break; + + case M_JFIF: + // Regular jpegs always have this tag, exif images have the exif + // marker instead, althogh ACDsee will write images with both markers. + // this program will re-create this marker on absence of exif marker. + // hence no need to keep the copy from the file. + free(Sections[--SectionsRead].Data); + Sections[SectionsRead].Data=0; + break; + + case M_EXIF: + // Seen files from some 'U-lead' software with Vivitar scanner + // that uses marker 31 for non exif stuff. Thus make sure + // it says 'Exif' in the section before treating it as exif. + if ((nReadMode & EXIF_READ_EXIF) && memcmp(Data+2, "Exif", 4) == 0){ + m_exifinfo->IsExif = process_EXIF((BYTE *)Data+2, itemlen); + }else{ + // Discard this section. + free(Sections[--SectionsRead].Data); + Sections[SectionsRead].Data=0; + } + break; + + case M_SOF0: + case M_SOF1: + case M_SOF2: + case M_SOF3: + case M_SOF5: + case M_SOF6: + case M_SOF7: + case M_SOF9: + case M_SOF10: + case M_SOF11: + case M_SOF13: + case M_SOF14: + case M_SOF15: + process_SOFn(Data, marker); + break; + default: + // Skip any other sections. + //if (ShowTags) printf("Jpeg section marker 0x%02x size %d\n",marker, itemlen); + break; + } + } + return true; +} +//////////////////////////////////////////////////////////////////////////////// +/*-------------------------------------------------------------------------- + Process a EXIF marker + Describes all the drivel that most digital cameras include... +--------------------------------------------------------------------------*/ +bool CxImageJPG::CxExifInfo::process_EXIF(unsigned char * CharBuf, unsigned int length) +{ + m_exifinfo->FlashUsed = 0; + /* If it's from a digicam, and it used flash, it says so. */ + m_exifinfo->Comments[0] = '\0'; /* Initial value - null string */ + + 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){ + strcpy(m_szLastError,"Invalid Exif start (1)"); + return false; + } + + int FirstOffset = Get32u(CharBuf+10); + /* + if (FirstOffset < 8 || FirstOffset > 16){ + // I used to ensure this was set to 8 (website I used indicated its 8) + // but PENTAX Optio 230 has it set differently, and uses it as offset. (Sept 11 2002) + strcpy(m_szLastError,"Suspicious offset of first IFD value"); + return false; + }*/ + + unsigned char * LastExifRefd = CharBuf; + + /* First directory starts 16 bytes in. Offsets start at 8 bytes in. */ + if (!ProcessExifDir(CharBuf+14, CharBuf+6, length-6, m_exifinfo, &LastExifRefd)) + return false; + + /* give a chance for a second directory */ + if (FirstOffset > 8) { + if (!ProcessExifDir(CharBuf+14+FirstOffset-8, CharBuf+6, length-6, m_exifinfo, &LastExifRefd)) + return false; + } + + /* This is how far the interesting (non thumbnail) part of the exif went. */ + // int ExifSettingsLength = LastExifRefd - CharBuf; + + /* Compute the CCD width, in milimeters. */ + if (m_exifinfo->FocalplaneXRes != 0){ + m_exifinfo->CCDWidth = (float)(ExifImageWidth * m_exifinfo->FocalplaneUnits / m_exifinfo->FocalplaneXRes); + } + + return true; +} +//-------------------------------------------------------------------------- +// Get 16 bits motorola order (always) for jpeg header stuff. +//-------------------------------------------------------------------------- +int CxImageJPG::CxExifInfo::Get16m(void * Short) +{ + return (((unsigned char *)Short)[0] << 8) | ((unsigned char *)Short)[1]; +} +//////////////////////////////////////////////////////////////////////////////// +/*-------------------------------------------------------------------------- + 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 +--------------------------------------------------------------------------*/ +long 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_VERSION 0x9000 +#define TAG_EXIF_OFFSET 0x8769 +#define TAG_INTEROP_OFFSET 0xa005 + +#define TAG_MAKE 0x010F +#define TAG_MODEL 0x0110 + +#define TAG_ORIENTATION 0x0112 +#define TAG_XRESOLUTION 0x011A +#define TAG_YRESOLUTION 0x011B +#define TAG_RESOLUTIONUNIT 0x0128 + +#define TAG_EXPOSURETIME 0x829A +#define TAG_FNUMBER 0x829D + +#define TAG_SHUTTERSPEED 0x9201 +#define TAG_APERTURE 0x9202 +#define TAG_BRIGHTNESS 0x9203 +#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_FOCALPLANEYRES 0xa20F +#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 m_exifinfo, unsigned char ** const LastExifRefdP, int NestingLevel) +{ + int de; + int a; + int NumDirEntries; + unsigned ThumbnailOffset = 0; + unsigned ThumbnailSize = 0; + + if (NestingLevel > 4){ + strcpy(m_szLastError,"Maximum directory nesting exceeded (corrupt exif header)"); + return false; + } + + 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(m_exifinfo->CameraMake, (char*)ValuePtr, 31); + break; + + case TAG_MODEL: + strncpy(m_exifinfo->CameraModel, (char*)ValuePtr, 39); + break; + + case TAG_EXIF_VERSION: + strncpy(m_exifinfo->Version,(char*)ValuePtr, 4); + break; + + case TAG_DATETIME_ORIGINAL: + strncpy(m_exifinfo->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(m_exifinfo->Comments, (char*)ValuePtr+a, 199); + break; + } + } + + }else{ + strncpy(m_exifinfo->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) + */ + m_exifinfo->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 (m_exifinfo->ApertureFNumber == 0){ + m_exifinfo->ApertureFNumber = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0f)*0.5); + } + break; + + case TAG_BRIGHTNESS: + m_exifinfo->Brightness = (float)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_FOCALLENGTH: + /* Nice digital cameras actually save the focal length + as a function of how farthey are zoomed in. + */ + + m_exifinfo->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. + */ + m_exifinfo->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) + */ + m_exifinfo->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 (m_exifinfo->ExposureTime == 0){ + m_exifinfo->ExposureTime = (float) + (1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0f))); + } + break; + + case TAG_FLASH: + if ((int)ConvertAnyFormat(ValuePtr, Format) & 7){ + m_exifinfo->FlashUsed = 1; + }else{ + m_exifinfo->FlashUsed = 0; + } + break; + + case TAG_ORIENTATION: + m_exifinfo->Orientation = (int)ConvertAnyFormat(ValuePtr, Format); + if (m_exifinfo->Orientation < 1 || m_exifinfo->Orientation > 8){ + strcpy(m_szLastError,"Undefined rotation value"); + m_exifinfo->Orientation = 0; + } + 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: + m_exifinfo->FocalplaneXRes = (float)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_FOCALPLANEYRES: + m_exifinfo->FocalplaneYRes = (float)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_RESOLUTIONUNIT: + switch((int)ConvertAnyFormat(ValuePtr, Format)){ + case 1: m_exifinfo->ResolutionUnit = 1.0f; break; /* 1 inch */ + case 2: m_exifinfo->ResolutionUnit = 1.0f; break; + case 3: m_exifinfo->ResolutionUnit = 0.3937007874f; break; /* 1 centimeter*/ + case 4: m_exifinfo->ResolutionUnit = 0.03937007874f; break; /* 1 millimeter*/ + case 5: m_exifinfo->ResolutionUnit = 0.00003937007874f; /* 1 micrometer*/ + } + break; + + case TAG_FOCALPLANEUNITS: + switch((int)ConvertAnyFormat(ValuePtr, Format)){ + case 1: m_exifinfo->FocalplaneUnits = 1.0f; break; /* 1 inch */ + case 2: m_exifinfo->FocalplaneUnits = 1.0f; break; + case 3: m_exifinfo->FocalplaneUnits = 0.3937007874f; break; /* 1 centimeter*/ + case 4: m_exifinfo->FocalplaneUnits = 0.03937007874f; break; /* 1 millimeter*/ + case 5: m_exifinfo->FocalplaneUnits = 0.00003937007874f; /* 1 micrometer*/ + } + break; + + // Remaining cases contributed by: Volker C. Schoech + + case TAG_EXPOSURE_BIAS: + m_exifinfo->ExposureBias = (float) ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_WHITEBALANCE: + m_exifinfo->Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_METERING_MODE: + m_exifinfo->MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_EXPOSURE_PROGRAM: + m_exifinfo->ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_ISO_EQUIVALENT: + m_exifinfo->ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format); + if ( m_exifinfo->ISOequivalent < 50 ) m_exifinfo->ISOequivalent *= 200; + break; + + case TAG_COMPRESSION_LEVEL: + m_exifinfo->CompressionLevel = (int)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_XRESOLUTION: + m_exifinfo->Xresolution = (float)ConvertAnyFormat(ValuePtr, Format); + break; + case TAG_YRESOLUTION: + m_exifinfo->Yresolution = (float)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; + unsigned Offset = Get32u(ValuePtr); + if (Offset>8){ + SubdirStart = OffsetBase + Offset; + if (SubdirStart < OffsetBase || + SubdirStart > OffsetBase+ExifLength){ + strcpy(m_szLastError,"Illegal subdirectory link"); + return false; + } + ProcessExifDir(SubdirStart, OffsetBase, ExifLength, m_exifinfo, LastExifRefdP, NestingLevel+1); + } + 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, m_exifinfo, LastExifRefdP, NestingLevel+1); + } + } + + + if (ThumbnailSize && ThumbnailOffset){ + if (ThumbnailSize + ThumbnailOffset <= ExifLength){ + /* The thumbnail pointer appears to be valid. Store it. */ + m_exifinfo->ThumbnailPointer = OffsetBase + ThumbnailOffset; + m_exifinfo->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; +} +//////////////////////////////////////////////////////////////////////////////// +void CxImageJPG::CxExifInfo::process_COM (const BYTE * Data, int length) +{ + int ch; + char Comment[MAX_COMMENT+1]; + int nch; + int a; + + nch = 0; + + if (length > MAX_COMMENT) length = MAX_COMMENT; // Truncate if it won't fit in our structure. + + for (a=2;aComments,Comment); +} +//////////////////////////////////////////////////////////////////////////////// +void CxImageJPG::CxExifInfo::process_SOFn (const BYTE * Data, int marker) +{ + int data_precision, num_components; + + data_precision = Data[2]; + m_exifinfo->Height = Get16m((void*)(Data+3)); + m_exifinfo->Width = Get16m((void*)(Data+5)); + num_components = Data[7]; + + if (num_components == 3){ + m_exifinfo->IsColor = 1; + }else{ + m_exifinfo->IsColor = 0; + } + + m_exifinfo->Process = marker; + + //if (ShowTags) printf("JPEG image is %uw * %uh, %d color components, %d bits per sample\n", + // ImageInfo.Width, ImageInfo.Height, num_components, data_precision); +} +//////////////////////////////////////////////////////////////////////////////// +/** + * this will work only on a CxImageJPG object, if the image originally has valid EXIF data + \verbatim + CxImageJPG jpg; + CxIOFile in,out; + in.Open("D:\\exif_in.jpg","rb"); + out.Open("D:\\exif_out.jpg","w+b"); + jpg.Decode(&in); + if (jpg.IsValid()){ + jpg.RotateLeft(); + jpg.Encode(&out); + } + \endverbatim +*/ +bool CxImageJPG::CxExifInfo::EncodeExif(CxFile * hFile) +{ + int a; + + if (FindSection(M_SOS)==NULL){ + strcpy(m_szLastError,"Can't write exif : didn't read all"); + return false; + } + + // Initial static jpeg marker. + hFile->PutC(0xff); + hFile->PutC(0xd8); + + if (Sections[0].Type != M_EXIF && Sections[0].Type != M_JFIF){ + // The image must start with an exif or jfif marker. If we threw those away, create one. + static BYTE JfifHead[18] = { + 0xff, M_JFIF, + 0x00, 0x10, 'J' , 'F' , 'I' , 'F' , 0x00, 0x01, + 0x01, 0x01, 0x01, 0x2C, 0x01, 0x2C, 0x00, 0x00 + }; + hFile->Write(JfifHead, 18, 1); + } + + // Write all the misc sections + for (a=0;aPutC(0xff); + hFile->PutC((unsigned char)(Sections[a].Type)); + hFile->Write(Sections[a].Data, Sections[a].Size, 1); + } + + // Write the remaining image data. + hFile->Write(Sections[a].Data, Sections[a].Size, 1); + + return true; +} +//////////////////////////////////////////////////////////////////////////////// +void CxImageJPG::CxExifInfo::DiscardAllButExif() +{ + Section_t ExifKeeper; + Section_t CommentKeeper; + int a; + + memset(&ExifKeeper, 0, sizeof(ExifKeeper)); + memset(&CommentKeeper, 0, sizeof(ExifKeeper)); + + for (a=0;a