--- /dev/null
+/*\r
+ * File: ximaexif.cpp\r
+ * Purpose: EXIF reader\r
+ * 18/Aug/2002 Davide Pizzolato - www.xdp.it\r
+ * CxImage version 6.0.0 02/Feb/2008\r
+ * based on jhead-1.8 by Matthias Wandel <mwandel(at)rim(dot)net>\r
+ */\r
+\r
+#include "ximajpg.h"\r
+\r
+#if CXIMAGEJPG_SUPPORT_EXIF\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+CxImageJPG::CxExifInfo::CxExifInfo(EXIFINFO* info)\r
+{\r
+ if (info) {\r
+ m_exifinfo = info;\r
+ freeinfo = false;\r
+ } else {\r
+ m_exifinfo = new EXIFINFO;\r
+ memset(m_exifinfo,0,sizeof(EXIFINFO));\r
+ freeinfo = true;\r
+ }\r
+\r
+ m_szLastError[0]='\0';\r
+ ExifImageWidth = MotorolaOrder = 0;\r
+ SectionsRead=0;\r
+ memset(&Sections, 0, MAX_SECTIONS * sizeof(Section_t));\r
+}\r
+////////////////////////////////////////////////////////////////////////////////\r
+CxImageJPG::CxExifInfo::~CxExifInfo()\r
+{\r
+ for(int i=0;i<MAX_SECTIONS;i++) if(Sections[i].Data) free(Sections[i].Data);\r
+ if (freeinfo) delete m_exifinfo;\r
+}\r
+////////////////////////////////////////////////////////////////////////////////\r
+bool CxImageJPG::CxExifInfo::DecodeExif(CxFile * hFile, int nReadMode)\r
+{\r
+ int a;\r
+ int HaveCom = FALSE;\r
+\r
+ a = hFile->GetC();\r
+\r
+ if (a != 0xff || hFile->GetC() != M_SOI){\r
+ return FALSE;\r
+ }\r
+\r
+ for(;;){\r
+ int itemlen;\r
+ int marker = 0;\r
+ int ll,lh, got;\r
+ BYTE * Data;\r
+\r
+ if (SectionsRead >= MAX_SECTIONS){\r
+ strcpy(m_szLastError,"Too many sections in jpg file");\r
+ return false;\r
+ }\r
+\r
+ for (a=0;a<7;a++){\r
+ marker = hFile->GetC();\r
+ if (marker != 0xff) break;\r
+\r
+ if (a >= 6){\r
+ printf("too many padding bytes\n");\r
+ return false;\r
+ }\r
+ }\r
+\r
+ if (marker == 0xff){\r
+ // 0xff is legal padding, but if we get that many, something's wrong.\r
+ strcpy(m_szLastError,"too many padding bytes!");\r
+ return false;\r
+ }\r
+\r
+ Sections[SectionsRead].Type = marker;\r
+\r
+ // Read the length of the section.\r
+ lh = hFile->GetC();\r
+ ll = hFile->GetC();\r
+\r
+ itemlen = (lh << 8) | ll;\r
+\r
+ if (itemlen < 2){\r
+ strcpy(m_szLastError,"invalid marker");\r
+ return false;\r
+ }\r
+\r
+ Sections[SectionsRead].Size = itemlen;\r
+\r
+ Data = (BYTE *)malloc(itemlen);\r
+ if (Data == NULL){\r
+ strcpy(m_szLastError,"Could not allocate memory");\r
+ return false;\r
+ }\r
+ Sections[SectionsRead].Data = Data;\r
+\r
+ // Store first two pre-read bytes.\r
+ Data[0] = (BYTE)lh;\r
+ Data[1] = (BYTE)ll;\r
+\r
+ got = hFile->Read(Data+2, 1, itemlen-2); // Read the whole section.\r
+ if (got != itemlen-2){\r
+ strcpy(m_szLastError,"Premature end of file?");\r
+ return false;\r
+ }\r
+ SectionsRead += 1;\r
+\r
+ switch(marker){\r
+\r
+ case M_SOS: // stop before hitting compressed data \r
+ // If reading entire image is requested, read the rest of the data.\r
+ if (nReadMode & EXIF_READ_IMAGE){\r
+ int cp, ep, size;\r
+ // Determine how much file is left.\r
+ cp = hFile->Tell();\r
+ hFile->Seek(0, SEEK_END);\r
+ ep = hFile->Tell();\r
+ hFile->Seek(cp, SEEK_SET);\r
+\r
+ size = ep-cp;\r
+ Data = (BYTE *)malloc(size);\r
+ if (Data == NULL){\r
+ strcpy(m_szLastError,"could not allocate data for entire image");\r
+ return false;\r
+ }\r
+\r
+ got = hFile->Read(Data, 1, size);\r
+ if (got != size){\r
+ strcpy(m_szLastError,"could not read the rest of the image");\r
+ return false;\r
+ }\r
+\r
+ Sections[SectionsRead].Data = Data;\r
+ Sections[SectionsRead].Size = size;\r
+ Sections[SectionsRead].Type = PSEUDO_IMAGE_MARKER;\r
+ SectionsRead ++;\r
+ }\r
+ return true;\r
+\r
+ case M_EOI: // in case it's a tables-only JPEG stream\r
+ printf("No image in jpeg!\n");\r
+ return FALSE;\r
+\r
+ case M_COM: // Comment section\r
+ if (HaveCom || ((nReadMode & EXIF_READ_EXIF) == 0)){\r
+ // Discard this section.\r
+ free(Sections[--SectionsRead].Data);\r
+ Sections[SectionsRead].Data=0;\r
+ }else{\r
+ process_COM(Data, itemlen);\r
+ HaveCom = TRUE;\r
+ }\r
+ break;\r
+\r
+ case M_JFIF:\r
+ // Regular jpegs always have this tag, exif images have the exif\r
+ // marker instead, althogh ACDsee will write images with both markers.\r
+ // this program will re-create this marker on absence of exif marker.\r
+ // hence no need to keep the copy from the file.\r
+ free(Sections[--SectionsRead].Data);\r
+ Sections[SectionsRead].Data=0;\r
+ break;\r
+\r
+ case M_EXIF:\r
+ // Seen files from some 'U-lead' software with Vivitar scanner\r
+ // that uses marker 31 for non exif stuff. Thus make sure \r
+ // it says 'Exif' in the section before treating it as exif.\r
+ if ((nReadMode & EXIF_READ_EXIF) && memcmp(Data+2, "Exif", 4) == 0){\r
+ m_exifinfo->IsExif = process_EXIF((BYTE *)Data+2, itemlen);\r
+ }else{\r
+ // Discard this section.\r
+ free(Sections[--SectionsRead].Data);\r
+ Sections[SectionsRead].Data=0;\r
+ }\r
+ break;\r
+\r
+ case M_SOF0: \r
+ case M_SOF1: \r
+ case M_SOF2: \r
+ case M_SOF3: \r
+ case M_SOF5: \r
+ case M_SOF6: \r
+ case M_SOF7: \r
+ case M_SOF9: \r
+ case M_SOF10:\r
+ case M_SOF11:\r
+ case M_SOF13:\r
+ case M_SOF14:\r
+ case M_SOF15:\r
+ process_SOFn(Data, marker);\r
+ break;\r
+ default:\r
+ // Skip any other sections.\r
+ //if (ShowTags) printf("Jpeg section marker 0x%02x size %d\n",marker, itemlen);\r
+ break;\r
+ }\r
+ }\r
+ return true;\r
+}\r
+////////////////////////////////////////////////////////////////////////////////\r
+/*--------------------------------------------------------------------------\r
+ Process a EXIF marker\r
+ Describes all the drivel that most digital cameras include...\r
+--------------------------------------------------------------------------*/\r
+bool CxImageJPG::CxExifInfo::process_EXIF(unsigned char * CharBuf, unsigned int length)\r
+{\r
+ m_exifinfo->FlashUsed = 0; \r
+ /* If it's from a digicam, and it used flash, it says so. */\r
+ m_exifinfo->Comments[0] = '\0'; /* Initial value - null string */\r
+\r
+ ExifImageWidth = 0;\r
+\r
+ { /* Check the EXIF header component */\r
+ static const unsigned char ExifHeader[] = "Exif\0\0";\r
+ if (memcmp(CharBuf+0, ExifHeader,6)){\r
+ strcpy(m_szLastError,"Incorrect Exif header");\r
+ return false;\r
+ }\r
+ }\r
+\r
+ if (memcmp(CharBuf+6,"II",2) == 0){\r
+ MotorolaOrder = 0;\r
+ }else{\r
+ if (memcmp(CharBuf+6,"MM",2) == 0){\r
+ MotorolaOrder = 1;\r
+ }else{\r
+ strcpy(m_szLastError,"Invalid Exif alignment marker.");\r
+ return false;\r
+ }\r
+ }\r
+\r
+ /* Check the next two values for correctness. */\r
+ if (Get16u(CharBuf+8) != 0x2a){\r
+ strcpy(m_szLastError,"Invalid Exif start (1)");\r
+ return false;\r
+ }\r
+\r
+ int FirstOffset = Get32u(CharBuf+10);\r
+ /* <Richard Collins> \r
+ if (FirstOffset < 8 || FirstOffset > 16){\r
+ // I used to ensure this was set to 8 (website I used indicated its 8)\r
+ // but PENTAX Optio 230 has it set differently, and uses it as offset. (Sept 11 2002)\r
+ strcpy(m_szLastError,"Suspicious offset of first IFD value");\r
+ return false;\r
+ }*/\r
+\r
+ unsigned char * LastExifRefd = CharBuf;\r
+\r
+ /* First directory starts 16 bytes in. Offsets start at 8 bytes in. */\r
+ if (!ProcessExifDir(CharBuf+14, CharBuf+6, length-6, m_exifinfo, &LastExifRefd))\r
+ return false;\r
+\r
+ /* <Richard Collins> give a chance for a second directory */\r
+ if (FirstOffset > 8) {\r
+ if (!ProcessExifDir(CharBuf+14+FirstOffset-8, CharBuf+6, length-6, m_exifinfo, &LastExifRefd))\r
+ return false;\r
+ }\r
+\r
+ /* This is how far the interesting (non thumbnail) part of the exif went. */\r
+ // int ExifSettingsLength = LastExifRefd - CharBuf;\r
+\r
+ /* Compute the CCD width, in milimeters. */\r
+ if (m_exifinfo->FocalplaneXRes != 0){\r
+ m_exifinfo->CCDWidth = (float)(ExifImageWidth * m_exifinfo->FocalplaneUnits / m_exifinfo->FocalplaneXRes);\r
+ }\r
+\r
+ return true;\r
+}\r
+//--------------------------------------------------------------------------\r
+// Get 16 bits motorola order (always) for jpeg header stuff.\r
+//--------------------------------------------------------------------------\r
+int CxImageJPG::CxExifInfo::Get16m(void * Short)\r
+{\r
+ return (((unsigned char *)Short)[0] << 8) | ((unsigned char *)Short)[1];\r
+}\r
+////////////////////////////////////////////////////////////////////////////////\r
+/*--------------------------------------------------------------------------\r
+ Convert a 16 bit unsigned value from file's native byte order\r
+--------------------------------------------------------------------------*/\r
+int CxImageJPG::CxExifInfo::Get16u(void * Short)\r
+{\r
+ if (MotorolaOrder){\r
+ return (((unsigned char *)Short)[0] << 8) | ((unsigned char *)Short)[1];\r
+ }else{\r
+ return (((unsigned char *)Short)[1] << 8) | ((unsigned char *)Short)[0];\r
+ }\r
+}\r
+////////////////////////////////////////////////////////////////////////////////\r
+/*--------------------------------------------------------------------------\r
+ Convert a 32 bit signed value from file's native byte order\r
+--------------------------------------------------------------------------*/\r
+long CxImageJPG::CxExifInfo::Get32s(void * Long)\r
+{\r
+ if (MotorolaOrder){\r
+ return ((( char *)Long)[0] << 24) | (((unsigned char *)Long)[1] << 16)\r
+ | (((unsigned char *)Long)[2] << 8 ) | (((unsigned char *)Long)[3] << 0 );\r
+ }else{\r
+ return ((( char *)Long)[3] << 24) | (((unsigned char *)Long)[2] << 16)\r
+ | (((unsigned char *)Long)[1] << 8 ) | (((unsigned char *)Long)[0] << 0 );\r
+ }\r
+}\r
+////////////////////////////////////////////////////////////////////////////////\r
+/*--------------------------------------------------------------------------\r
+ Convert a 32 bit unsigned value from file's native byte order\r
+--------------------------------------------------------------------------*/\r
+unsigned long CxImageJPG::CxExifInfo::Get32u(void * Long)\r
+{\r
+ return (unsigned long)Get32s(Long) & 0xffffffff;\r
+}\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+/* Describes format descriptor */\r
+static const int BytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8};\r
+#define NUM_FORMATS 12\r
+\r
+#define FMT_BYTE 1 \r
+#define FMT_STRING 2\r
+#define FMT_USHORT 3\r
+#define FMT_ULONG 4\r
+#define FMT_URATIONAL 5\r
+#define FMT_SBYTE 6\r
+#define FMT_UNDEFINED 7\r
+#define FMT_SSHORT 8\r
+#define FMT_SLONG 9\r
+#define FMT_SRATIONAL 10\r
+#define FMT_SINGLE 11\r
+#define FMT_DOUBLE 12\r
+\r
+/* Describes tag values */\r
+\r
+#define TAG_EXIF_VERSION 0x9000\r
+#define TAG_EXIF_OFFSET 0x8769\r
+#define TAG_INTEROP_OFFSET 0xa005\r
+\r
+#define TAG_MAKE 0x010F\r
+#define TAG_MODEL 0x0110\r
+\r
+#define TAG_ORIENTATION 0x0112\r
+#define TAG_XRESOLUTION 0x011A\r
+#define TAG_YRESOLUTION 0x011B\r
+#define TAG_RESOLUTIONUNIT 0x0128\r
+\r
+#define TAG_EXPOSURETIME 0x829A\r
+#define TAG_FNUMBER 0x829D\r
+\r
+#define TAG_SHUTTERSPEED 0x9201\r
+#define TAG_APERTURE 0x9202\r
+#define TAG_BRIGHTNESS 0x9203\r
+#define TAG_MAXAPERTURE 0x9205\r
+#define TAG_FOCALLENGTH 0x920A\r
+\r
+#define TAG_DATETIME_ORIGINAL 0x9003\r
+#define TAG_USERCOMMENT 0x9286\r
+\r
+#define TAG_SUBJECT_DISTANCE 0x9206\r
+#define TAG_FLASH 0x9209\r
+\r
+#define TAG_FOCALPLANEXRES 0xa20E\r
+#define TAG_FOCALPLANEYRES 0xa20F\r
+#define TAG_FOCALPLANEUNITS 0xa210\r
+#define TAG_EXIF_IMAGEWIDTH 0xA002\r
+#define TAG_EXIF_IMAGELENGTH 0xA003\r
+\r
+/* the following is added 05-jan-2001 vcs */\r
+#define TAG_EXPOSURE_BIAS 0x9204\r
+#define TAG_WHITEBALANCE 0x9208\r
+#define TAG_METERING_MODE 0x9207\r
+#define TAG_EXPOSURE_PROGRAM 0x8822\r
+#define TAG_ISO_EQUIVALENT 0x8827\r
+#define TAG_COMPRESSION_LEVEL 0x9102\r
+\r
+#define TAG_THUMBNAIL_OFFSET 0x0201\r
+#define TAG_THUMBNAIL_LENGTH 0x0202\r
+\r
+\r
+/*--------------------------------------------------------------------------\r
+ Process one of the nested EXIF directories.\r
+--------------------------------------------------------------------------*/\r
+bool CxImageJPG::CxExifInfo::ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, unsigned ExifLength,\r
+ EXIFINFO * const m_exifinfo, unsigned char ** const LastExifRefdP, int NestingLevel)\r
+{\r
+ int de;\r
+ int a;\r
+ int NumDirEntries;\r
+ unsigned ThumbnailOffset = 0;\r
+ unsigned ThumbnailSize = 0;\r
+\r
+ if (NestingLevel > 4){\r
+ strcpy(m_szLastError,"Maximum directory nesting exceeded (corrupt exif header)");\r
+ return false;\r
+ }\r
+\r
+ NumDirEntries = Get16u(DirStart);\r
+\r
+ if ((DirStart+2+NumDirEntries*12) > (OffsetBase+ExifLength)){\r
+ strcpy(m_szLastError,"Illegally sized directory");\r
+ return false;\r
+ }\r
+\r
+ for (de=0;de<NumDirEntries;de++){\r
+ int Tag, Format, Components;\r
+ unsigned char * ValuePtr;\r
+ /* This actually can point to a variety of things; it must be\r
+ cast to other types when used. But we use it as a byte-by-byte\r
+ cursor, so we declare it as a pointer to a generic byte here.\r
+ */\r
+ int ByteCount;\r
+ unsigned char * DirEntry;\r
+ DirEntry = DirStart+2+12*de;\r
+\r
+ Tag = Get16u(DirEntry);\r
+ Format = Get16u(DirEntry+2);\r
+ Components = Get32u(DirEntry+4);\r
+\r
+ if ((Format-1) >= NUM_FORMATS) {\r
+ /* (-1) catches illegal zero case as unsigned underflows to positive large */\r
+ strcpy(m_szLastError,"Illegal format code in EXIF dir");\r
+ return false;\r
+ }\r
+\r
+ ByteCount = Components * BytesPerFormat[Format];\r
+\r
+ if (ByteCount > 4){\r
+ unsigned OffsetVal;\r
+ OffsetVal = Get32u(DirEntry+8);\r
+ /* If its bigger than 4 bytes, the dir entry contains an offset.*/\r
+ if (OffsetVal+ByteCount > ExifLength){\r
+ /* Bogus pointer offset and / or bytecount value */\r
+ strcpy(m_szLastError,"Illegal pointer offset value in EXIF.");\r
+ return false;\r
+ }\r
+ ValuePtr = OffsetBase+OffsetVal;\r
+ }else{\r
+ /* 4 bytes or less and value is in the dir entry itself */\r
+ ValuePtr = DirEntry+8;\r
+ }\r
+\r
+ if (*LastExifRefdP < ValuePtr+ByteCount){\r
+ /* Keep track of last byte in the exif header that was\r
+ actually referenced. That way, we know where the\r
+ discardable thumbnail data begins.\r
+ */\r
+ *LastExifRefdP = ValuePtr+ByteCount;\r
+ }\r
+\r
+ /* Extract useful components of tag */\r
+ switch(Tag){\r
+\r
+ case TAG_MAKE:\r
+ strncpy(m_exifinfo->CameraMake, (char*)ValuePtr, 31);\r
+ break;\r
+\r
+ case TAG_MODEL:\r
+ strncpy(m_exifinfo->CameraModel, (char*)ValuePtr, 39);\r
+ break;\r
+\r
+ case TAG_EXIF_VERSION:\r
+ strncpy(m_exifinfo->Version,(char*)ValuePtr, 4);\r
+ break;\r
+\r
+ case TAG_DATETIME_ORIGINAL:\r
+ strncpy(m_exifinfo->DateTime, (char*)ValuePtr, 19);\r
+ break;\r
+\r
+ case TAG_USERCOMMENT:\r
+ // Olympus has this padded with trailing spaces. Remove these first. \r
+ for (a=ByteCount;;){\r
+ a--;\r
+ if (((char*)ValuePtr)[a] == ' '){\r
+ ((char*)ValuePtr)[a] = '\0';\r
+ }else{\r
+ break;\r
+ }\r
+ if (a == 0) break;\r
+ }\r
+\r
+ /* Copy the comment */\r
+ if (memcmp(ValuePtr, "ASCII",5) == 0){\r
+ for (a=5;a<10;a++){\r
+ char c;\r
+ c = ((char*)ValuePtr)[a];\r
+ if (c != '\0' && c != ' '){\r
+ strncpy(m_exifinfo->Comments, (char*)ValuePtr+a, 199);\r
+ break;\r
+ }\r
+ }\r
+ \r
+ }else{\r
+ strncpy(m_exifinfo->Comments, (char*)ValuePtr, 199);\r
+ }\r
+ break;\r
+\r
+ case TAG_FNUMBER:\r
+ /* Simplest way of expressing aperture, so I trust it the most.\r
+ (overwrite previously computd value if there is one)\r
+ */\r
+ m_exifinfo->ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format);\r
+ break;\r
+\r
+ case TAG_APERTURE:\r
+ case TAG_MAXAPERTURE:\r
+ /* More relevant info always comes earlier, so only\r
+ use this field if we don't have appropriate aperture\r
+ information yet. \r
+ */\r
+ if (m_exifinfo->ApertureFNumber == 0){\r
+ m_exifinfo->ApertureFNumber = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0f)*0.5);\r
+ }\r
+ break;\r
+\r
+ case TAG_BRIGHTNESS:\r
+ m_exifinfo->Brightness = (float)ConvertAnyFormat(ValuePtr, Format);\r
+ break;\r
+\r
+ case TAG_FOCALLENGTH:\r
+ /* Nice digital cameras actually save the focal length\r
+ as a function of how farthey are zoomed in. \r
+ */\r
+\r
+ m_exifinfo->FocalLength = (float)ConvertAnyFormat(ValuePtr, Format);\r
+ break;\r
+\r
+ case TAG_SUBJECT_DISTANCE:\r
+ /* Inidcates the distacne the autofocus camera is focused to.\r
+ Tends to be less accurate as distance increases.\r
+ */\r
+ m_exifinfo->Distance = (float)ConvertAnyFormat(ValuePtr, Format);\r
+ break;\r
+\r
+ case TAG_EXPOSURETIME:\r
+ /* Simplest way of expressing exposure time, so I\r
+ trust it most. (overwrite previously computd value\r
+ if there is one) \r
+ */\r
+ m_exifinfo->ExposureTime = \r
+ (float)ConvertAnyFormat(ValuePtr, Format);\r
+ break;\r
+\r
+ case TAG_SHUTTERSPEED:\r
+ /* More complicated way of expressing exposure time,\r
+ so only use this value if we don't already have it\r
+ from somewhere else. \r
+ */\r
+ if (m_exifinfo->ExposureTime == 0){\r
+ m_exifinfo->ExposureTime = (float)\r
+ (1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0f)));\r
+ }\r
+ break;\r
+\r
+ case TAG_FLASH:\r
+ if ((int)ConvertAnyFormat(ValuePtr, Format) & 7){\r
+ m_exifinfo->FlashUsed = 1;\r
+ }else{\r
+ m_exifinfo->FlashUsed = 0;\r
+ }\r
+ break;\r
+\r
+ case TAG_ORIENTATION:\r
+ m_exifinfo->Orientation = (int)ConvertAnyFormat(ValuePtr, Format);\r
+ if (m_exifinfo->Orientation < 1 || m_exifinfo->Orientation > 8){\r
+ strcpy(m_szLastError,"Undefined rotation value");\r
+ m_exifinfo->Orientation = 0;\r
+ }\r
+ break;\r
+\r
+ case TAG_EXIF_IMAGELENGTH:\r
+ case TAG_EXIF_IMAGEWIDTH:\r
+ /* Use largest of height and width to deal with images\r
+ that have been rotated to portrait format. \r
+ */\r
+ a = (int)ConvertAnyFormat(ValuePtr, Format);\r
+ if (ExifImageWidth < a) ExifImageWidth = a;\r
+ break;\r
+\r
+ case TAG_FOCALPLANEXRES:\r
+ m_exifinfo->FocalplaneXRes = (float)ConvertAnyFormat(ValuePtr, Format);\r
+ break;\r
+\r
+ case TAG_FOCALPLANEYRES:\r
+ m_exifinfo->FocalplaneYRes = (float)ConvertAnyFormat(ValuePtr, Format);\r
+ break;\r
+\r
+ case TAG_RESOLUTIONUNIT:\r
+ switch((int)ConvertAnyFormat(ValuePtr, Format)){\r
+ case 1: m_exifinfo->ResolutionUnit = 1.0f; break; /* 1 inch */\r
+ case 2: m_exifinfo->ResolutionUnit = 1.0f; break;\r
+ case 3: m_exifinfo->ResolutionUnit = 0.3937007874f; break; /* 1 centimeter*/\r
+ case 4: m_exifinfo->ResolutionUnit = 0.03937007874f; break; /* 1 millimeter*/\r
+ case 5: m_exifinfo->ResolutionUnit = 0.00003937007874f; /* 1 micrometer*/\r
+ }\r
+ break;\r
+\r
+ case TAG_FOCALPLANEUNITS:\r
+ switch((int)ConvertAnyFormat(ValuePtr, Format)){\r
+ case 1: m_exifinfo->FocalplaneUnits = 1.0f; break; /* 1 inch */\r
+ case 2: m_exifinfo->FocalplaneUnits = 1.0f; break;\r
+ case 3: m_exifinfo->FocalplaneUnits = 0.3937007874f; break; /* 1 centimeter*/\r
+ case 4: m_exifinfo->FocalplaneUnits = 0.03937007874f; break; /* 1 millimeter*/\r
+ case 5: m_exifinfo->FocalplaneUnits = 0.00003937007874f; /* 1 micrometer*/\r
+ }\r
+ break;\r
+\r
+ // Remaining cases contributed by: Volker C. Schoech <schoech(at)gmx(dot)de>\r
+\r
+ case TAG_EXPOSURE_BIAS:\r
+ m_exifinfo->ExposureBias = (float) ConvertAnyFormat(ValuePtr, Format);\r
+ break;\r
+\r
+ case TAG_WHITEBALANCE:\r
+ m_exifinfo->Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format);\r
+ break;\r
+\r
+ case TAG_METERING_MODE:\r
+ m_exifinfo->MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format);\r
+ break;\r
+\r
+ case TAG_EXPOSURE_PROGRAM:\r
+ m_exifinfo->ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format);\r
+ break;\r
+\r
+ case TAG_ISO_EQUIVALENT:\r
+ m_exifinfo->ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);\r
+ if ( m_exifinfo->ISOequivalent < 50 ) m_exifinfo->ISOequivalent *= 200;\r
+ break;\r
+\r
+ case TAG_COMPRESSION_LEVEL:\r
+ m_exifinfo->CompressionLevel = (int)ConvertAnyFormat(ValuePtr, Format);\r
+ break;\r
+\r
+ case TAG_XRESOLUTION:\r
+ m_exifinfo->Xresolution = (float)ConvertAnyFormat(ValuePtr, Format);\r
+ break;\r
+ case TAG_YRESOLUTION:\r
+ m_exifinfo->Yresolution = (float)ConvertAnyFormat(ValuePtr, Format);\r
+ break;\r
+\r
+ case TAG_THUMBNAIL_OFFSET:\r
+ ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format);\r
+ break;\r
+\r
+ case TAG_THUMBNAIL_LENGTH:\r
+ ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format);\r
+ break;\r
+\r
+ }\r
+\r
+ if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET){\r
+ unsigned char * SubdirStart;\r
+ unsigned Offset = Get32u(ValuePtr);\r
+ if (Offset>8){\r
+ SubdirStart = OffsetBase + Offset;\r
+ if (SubdirStart < OffsetBase || \r
+ SubdirStart > OffsetBase+ExifLength){\r
+ strcpy(m_szLastError,"Illegal subdirectory link");\r
+ return false;\r
+ }\r
+ ProcessExifDir(SubdirStart, OffsetBase, ExifLength, m_exifinfo, LastExifRefdP, NestingLevel+1);\r
+ }\r
+ continue;\r
+ }\r
+ }\r
+\r
+\r
+ {\r
+ /* In addition to linking to subdirectories via exif tags,\r
+ there's also a potential link to another directory at the end\r
+ of each directory. This has got to be the result of a\r
+ committee! \r
+ */\r
+ unsigned char * SubdirStart;\r
+ unsigned Offset;\r
+ Offset = Get16u(DirStart+2+12*NumDirEntries);\r
+ if (Offset){\r
+ SubdirStart = OffsetBase + Offset;\r
+ if (SubdirStart < OffsetBase \r
+ || SubdirStart > OffsetBase+ExifLength){\r
+ strcpy(m_szLastError,"Illegal subdirectory link");\r
+ return false;\r
+ }\r
+ ProcessExifDir(SubdirStart, OffsetBase, ExifLength, m_exifinfo, LastExifRefdP, NestingLevel+1);\r
+ }\r
+ }\r
+\r
+\r
+ if (ThumbnailSize && ThumbnailOffset){\r
+ if (ThumbnailSize + ThumbnailOffset <= ExifLength){\r
+ /* The thumbnail pointer appears to be valid. Store it. */\r
+ m_exifinfo->ThumbnailPointer = OffsetBase + ThumbnailOffset;\r
+ m_exifinfo->ThumbnailSize = ThumbnailSize;\r
+ }\r
+ }\r
+\r
+ return true;\r
+}\r
+////////////////////////////////////////////////////////////////////////////////\r
+/*--------------------------------------------------------------------------\r
+ Evaluate number, be it int, rational, or float from directory.\r
+--------------------------------------------------------------------------*/\r
+double CxImageJPG::CxExifInfo::ConvertAnyFormat(void * ValuePtr, int Format)\r
+{\r
+ double Value;\r
+ Value = 0;\r
+\r
+ switch(Format){\r
+ case FMT_SBYTE: Value = *(signed char *)ValuePtr; break;\r
+ case FMT_BYTE: Value = *(unsigned char *)ValuePtr; break;\r
+\r
+ case FMT_USHORT: Value = Get16u(ValuePtr); break;\r
+ case FMT_ULONG: Value = Get32u(ValuePtr); break;\r
+\r
+ case FMT_URATIONAL:\r
+ case FMT_SRATIONAL: \r
+ {\r
+ int Num,Den;\r
+ Num = Get32s(ValuePtr);\r
+ Den = Get32s(4+(char *)ValuePtr);\r
+ if (Den == 0){\r
+ Value = 0;\r
+ }else{\r
+ Value = (double)Num/Den;\r
+ }\r
+ break;\r
+ }\r
+\r
+ case FMT_SSHORT: Value = (signed short)Get16u(ValuePtr); break;\r
+ case FMT_SLONG: Value = Get32s(ValuePtr); break;\r
+\r
+ /* Not sure if this is correct (never seen float used in Exif format)\r
+ */\r
+ case FMT_SINGLE: Value = (double)*(float *)ValuePtr; break;\r
+ case FMT_DOUBLE: Value = *(double *)ValuePtr; break;\r
+ }\r
+ return Value;\r
+}\r
+////////////////////////////////////////////////////////////////////////////////\r
+void CxImageJPG::CxExifInfo::process_COM (const BYTE * Data, int length)\r
+{\r
+ int ch;\r
+ char Comment[MAX_COMMENT+1];\r
+ int nch;\r
+ int a;\r
+\r
+ nch = 0;\r
+\r
+ if (length > MAX_COMMENT) length = MAX_COMMENT; // Truncate if it won't fit in our structure.\r
+\r
+ for (a=2;a<length;a++){\r
+ ch = Data[a];\r
+\r
+ if (ch == '\r' && Data[a+1] == '\n') continue; // Remove cr followed by lf.\r
+\r
+ if (isprint(ch) || ch == '\n' || ch == '\t'){\r
+ Comment[nch++] = (char)ch;\r
+ }else{\r
+ Comment[nch++] = '?';\r
+ }\r
+ }\r
+\r
+ Comment[nch] = '\0'; // Null terminate\r
+\r
+ //if (ShowTags) printf("COM marker comment: %s\n",Comment);\r
+\r
+ strcpy(m_exifinfo->Comments,Comment);\r
+}\r
+////////////////////////////////////////////////////////////////////////////////\r
+void CxImageJPG::CxExifInfo::process_SOFn (const BYTE * Data, int marker)\r
+{\r
+ int data_precision, num_components;\r
+\r
+ data_precision = Data[2];\r
+ m_exifinfo->Height = Get16m((void*)(Data+3));\r
+ m_exifinfo->Width = Get16m((void*)(Data+5));\r
+ num_components = Data[7];\r
+\r
+ if (num_components == 3){\r
+ m_exifinfo->IsColor = 1;\r
+ }else{\r
+ m_exifinfo->IsColor = 0;\r
+ }\r
+\r
+ m_exifinfo->Process = marker;\r
+\r
+ //if (ShowTags) printf("JPEG image is %uw * %uh, %d color components, %d bits per sample\n",\r
+ // ImageInfo.Width, ImageInfo.Height, num_components, data_precision);\r
+}\r
+////////////////////////////////////////////////////////////////////////////////\r
+/**\r
+ * this will work only on a CxImageJPG object, if the image originally has valid EXIF data\r
+ \verbatim\r
+ CxImageJPG jpg;\r
+ CxIOFile in,out;\r
+ in.Open("D:\\exif_in.jpg","rb");\r
+ out.Open("D:\\exif_out.jpg","w+b");\r
+ jpg.Decode(&in);\r
+ if (jpg.IsValid()){\r
+ jpg.RotateLeft();\r
+ jpg.Encode(&out);\r
+ }\r
+ \endverbatim\r
+*/\r
+bool CxImageJPG::CxExifInfo::EncodeExif(CxFile * hFile)\r
+{\r
+ int a;\r
+\r
+ if (FindSection(M_SOS)==NULL){\r
+ strcpy(m_szLastError,"Can't write exif : didn't read all");\r
+ return false;\r
+ }\r
+\r
+ // Initial static jpeg marker.\r
+ hFile->PutC(0xff);\r
+ hFile->PutC(0xd8);\r
+ \r
+ if (Sections[0].Type != M_EXIF && Sections[0].Type != M_JFIF){\r
+ // The image must start with an exif or jfif marker. If we threw those away, create one.\r
+ static BYTE JfifHead[18] = {\r
+ 0xff, M_JFIF,\r
+ 0x00, 0x10, 'J' , 'F' , 'I' , 'F' , 0x00, 0x01, \r
+ 0x01, 0x01, 0x01, 0x2C, 0x01, 0x2C, 0x00, 0x00 \r
+ };\r
+ hFile->Write(JfifHead, 18, 1);\r
+ }\r
+\r
+ // Write all the misc sections\r
+ for (a=0;a<SectionsRead-1;a++){\r
+ hFile->PutC(0xff);\r
+ hFile->PutC((unsigned char)(Sections[a].Type));\r
+ hFile->Write(Sections[a].Data, Sections[a].Size, 1);\r
+ }\r
+\r
+ // Write the remaining image data.\r
+ hFile->Write(Sections[a].Data, Sections[a].Size, 1);\r
+\r
+ return true;\r
+}\r
+////////////////////////////////////////////////////////////////////////////////\r
+void CxImageJPG::CxExifInfo::DiscardAllButExif()\r
+{\r
+ Section_t ExifKeeper;\r
+ Section_t CommentKeeper;\r
+ int a;\r
+\r
+ memset(&ExifKeeper, 0, sizeof(ExifKeeper));\r
+ memset(&CommentKeeper, 0, sizeof(ExifKeeper));\r
+\r
+ for (a=0;a<SectionsRead;a++){\r
+ if (Sections[a].Type == M_EXIF && ExifKeeper.Type == 0){\r
+ ExifKeeper = Sections[a];\r
+ }else if (Sections[a].Type == M_COM && CommentKeeper.Type == 0){\r
+ CommentKeeper = Sections[a];\r
+ }else{\r
+ free(Sections[a].Data);\r
+ Sections[a].Data = 0;\r
+ }\r
+ }\r
+ SectionsRead = 0;\r
+ if (ExifKeeper.Type){\r
+ Sections[SectionsRead++] = ExifKeeper;\r
+ }\r
+ if (CommentKeeper.Type){\r
+ Sections[SectionsRead++] = CommentKeeper;\r
+ }\r
+}\r
+////////////////////////////////////////////////////////////////////////////////\r
+void* CxImageJPG::CxExifInfo::FindSection(int SectionType)\r
+{\r
+ int a;\r
+ for (a=0;a<SectionsRead-1;a++){\r
+ if (Sections[a].Type == SectionType){\r
+ return &Sections[a];\r
+ }\r
+ }\r
+ // Could not be found.\r
+ return NULL;\r
+}\r
+////////////////////////////////////////////////////////////////////////////////\r
+#endif // CXIMAGEJPG_SUPPORT_EXIF\r
+\r