// .h 文件
/******************************************************************** Copyright@ 版权所有@ 1998-2005hengai。保留所有权利。
********************************************************************/
/******************************************************************** 文件说明: 能够读取 JPG 图像文件中的 EXIF 信息
文件名称: exif.h
版本号 : 1.0.0
作者: hengai
修改纪录:
使用方法: 包含此头文件,然后调用函数
int EXIF_Read(LPCTSTR pszJpgFileName, EXIFINFO* pExifIn fo)
即可获取 EXIF 信息。EXIF信息包含在参数 pExifInfo 中在定义了 #define EXIF_OUTPUT_ERRMSG (默认下已经定义)后可以使用
LPCTSTR EXIF_GetErrorString(); 获取出错信息
*********************************************************************
///////////////////////////////////////////////////////////////////// /////
#ifndef STATIC
#define STATIC static
#endif
#define EXIF_OUTPUT_ERRMSG//定义是否输出出错信息
///////////////////////////////////////////////////////////////////// /////
// 定义常量
#define MAX_COMMENT1000//最大的注释字符串长度
///////////////////////////////////////////////////////////////////// /////
//写入注释时,表明注释的类型,如 ASCII, UNICODE 等
typedef enum ECT{
CT_ASCII = 0,
CT_UNICODE,
CT_JIS,
CT_UNDEFINE
}COMMENT_TYPE;
///////////////////////////////////////////////////////////////////// /////
// 定义需要的结构体
#define ET_NOT_CLOSE_File0x00000001//最后不关闭打开的文件句柄
#define ET_MALLOC_THUMBNAIL0x00000002//拷贝缩略图的数据,调用者需要使用 free()
#define ET_MALLOC_USERCOM0x00000004//是否拷贝用户注释,调用者需要使用 free()
#define ET_MALLOC_MAKERCOM0x00000008//是否拷贝厂商注释,调用者需要使用 free()
//JPG 文件中的读入后的 EXIFF 信息保存到这个结构体中
typedef struct tag_ExifInfo {
DWORD dwExifType; //取值为 ET_NOT_CLOSE_File|ET_MALLOC_ THUMBNAIL, ....
DWORD dwExifType2;
char Version [5]; //EXIF 信息版本
char CameraMake [32]; //DC 制造商
char CameraModel [40]; //DC 型号
char DateTime [20]; //JPG 文件日期
char DateTimeDigitized[20]; //JPG 文件被其它软件修改日期
int Height, Width; //图像高度、宽度
int Orientation; //拍摄方向,例如相机向左手方向旋转后拍摄的
int IsColor; //
int Process; //被处理
int FlashUsed; //是否使用闪光灯
float FocalLength; //焦距
float ExposureTime; //曝光时间(快门速度)
float ApertureFNumber; //光圈数
float Distance; //拍摄物体距离
float CCDWidth; //CCD 大小
float ExposureBias; //曝光补偿
int Whitebalance; //白平衡
int MeteringMode; //测光模式
int ExposureProgram; //曝光
int ISOequivalent; //ISO
int CompressionLevel; //压缩
float FocalplaneXRes; //焦平面X轴分辨率
float FocalplaneYRes; //焦平面Y轴分辨率
float FocalplaneUnits; //焦平面分辨率单位
float Xresolution; //X 轴分辨率
float Yresolution; //Y 轴分辨率
float ResolutionUnit; //分辨率单位
float Brightness; //亮度
char Comments[MAX_COMMENT]; //注释
DWORD UserCOMLength; //用户注释长度。如果==0表示没有用户注释
char *UserCOM; //用户注释
//if(dwExifType&ET_MALLOC_USERCOM == TRUE) 这个数值保存了用户注释数据,调用者需要使用 free()
//否则为用户注释的偏移量(相对于文件起始0处)
DWORD MakerCOMLength; //厂商注释长度。如果==0表示没有厂商注释
char *MakerCOM; //厂商注释
//if(dwExifType&ET_MALLOC_MAKERCOM == TRUE) 这个数值保存了厂商注释数据,调用者需要使用 free()
//否则为厂商注释的偏移量(注意:是在当前SECTION中的偏移量,不是相对整个文件的)
UCHAR * ThumbnailPointer; //缩略图数据。
//if(dwExifType&ET_MALLOC_THUMBNAIL = = TRUE) 这个数值保存了缩略图的数据
//否则为一个 DWORD(需要强制转换) 表示缩略图在JPG文件中的偏移值(相对于文件起始0处)
DWORD ThumbnailSize; //缩略图的大小(字节流 ThumbnailPointe r 的长度)
//如果<=0表示该 JPG 文件没有缩略图HFile hJpgFileHandle; //返回打开的 JPG 文件句柄。必须 dwExi fType&ET_NOT_CLOSE_File == TRUE 才是有效句柄
//用户需要使用 CloseHandle(hJpgFileHa ndle)来关闭这个句柄
BOOL IsExif; //是否存在 EXIF 信息
} EXIFINFO;
///////////////////////////////////////////////////////////////////// /////
// 接口函数
int EXIF_Read(LPCTSTR pszJpgFileName, EXIFINFO* pExifInfo); LPCTSTR EXIF_GetErrorString();
int EXIF_AddUserComments(LPCTSTR pszJpgFileName, LPCTSTR
pszUserComme nts, DWORD dwCommentLength, COMMENT_TYPE nCommentType);
////////////////////////////////////////////////////////////////////////
// .c 文件
/******************************************************************** Copyright@ 版权所有@ 1998-2005 HENGAI。保留所有权利。
********************************************************************/
/******************************************************************** 文件说明:
文件名称: exif.c
版本号 : 1.0.0
作者: hengai
修改纪录:
********************************************************************* /
#include \"exif.h\"
///////////////////////////////////////////////////////////////////// /////
//读取 EXIF 过程中需要的结构体
typedef struct tag_Section_t{
UCHAR *Data;
int Type;
unsigned Size;
} Section_t;
///////////////////////////////////////////////////////////////////// /////
#ifdef EXIF_OUTPUT_ERRMSG
STATIC TCHAR m_szLastError[256]; //这里保存了出错的信息
#define EXIF_ERR_OUT(str_err) strcpy(m_szLastError,str_err);
LPCTSTR EXIF_GetErrorString()
{
return (LPCTSTR)m_szLastError;
}
#else
#define EXIF_ERR_OUT
LPCTSTR EXIF_GetErrrorString()
{
return\"Plese #define EXIT_ERR_OUT in exif.h\";
}
#endif
STATIC EXIFINFO* m_pExifInfo = 0; //
STATIC int m_MotorolaOrder = 0; //
STATIC int m_ExifImageWidth = 0; //
///////////////////////////////////////////////////////////////////// /////
/* Describes format descriptor */
static const int m_BytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8}; #define NUM_FORMATS12
#define FMT_BYTE1//Format Byte
#define FMT_STRING2
#define FMT_USHORT3
#define FMT_ULONG4
#define FMT_URATIONAL5
#define FMT_SBYTE6
#define FMT_UNDEFINED7
#define FMT_SSHORT8
#define FMT_SLONG9
#define FMT_SRATIONAL10
#define FMT_SINGLE11
#define FMT_DOUBLE12
///////////////////////////////////////////////////////////////////// /////
#define MAX_SECTIONS20//JPG 文件中能够允许的最多 SECTION 个数
#ifndef M_SOI
#define M_SOF00xC0// Start Of Frame N
#define M_SOF10xC1// N indicates which compression proc ess
#define M_SOF20xC2// Only SOF0-SOF2 are now in common u se
#define M_SOF30xC3
#define M_SOF50xC5// NB: codes C4 and CC are NOT SOF ma rkers
#define M_SOF60xC6
#define M_SOF70xC7
#define M_SOF90xC9
#define M_SOF100xCA
#define M_SOF110xCB
#define M_SOF130xCD
#define M_SOF140xCE
#define M_SOF150xCF
#define M_SOI0xD8// Start Of Image (beginning of datas tream)
#define M_EOI0xD9// End Of Image (end of datastream) #define M_SOS0xDA// Start Of Scan (begins compressed d ata)
#define M_JFIF0xE0// Jfif marker
#define M_EXIF0xE1// Exif marker
#define M_COM0xFE// COMment
//定义 APP 标识(SECTION)
#define M_APP00xE0
#define M_APP10xE1
#define M_APP20xE2
#define M_APP30xE3
#define M_APP40xE4
#define M_APP50xE5
#define M_APP60xE6
//...
#endif
// Describes tag values
//注意: 下面的定义是按照 Intel CPU 来定义的,也就是说所有的都是高位在后,
//这样的定义可能与 EXIF 上的定义不一致。例如上把 TAG_MAKE 定义为 0F01
//下面是主要信息
#define TAG_MAKE0x010F//相机DC 制造商
#define TAG_MODEL0x0110//DC 型号
#define TAG_ORIENTATION0x0112//拍摄时方向,例如向左手旋转D C 90度拍摄照片
#define TAG_XRESOLUTION0x011A//X 轴分辨率
#define TAG_YRESOLUTION0x011B//Y 轴分辨率
#define TAG_RESOLUTIONUNIT0x0128//分辨率单位,例如 inch, cm #define TAG_DATATIME0x0132//日期时间
#define TAG_YBCR_POSITION0x0213//YCbCr 位置控制,例如居中#define TAG_COPYRIGHT0x8298//版权
#define TAG_EXIF_OFFSET0x8769//EXIF 偏移,这时候相当于处理一个新的 EXIF 信息
//
#define TAG_IMAGEWIDTH0x0001//图像宽度
#define TAG_IMAGEHEIGHT0x0101//图像高度
//BOOKMARK
//辅助信息
#define TAG_EXPOSURETIME0x829A//曝光时间,例如 1/30 秒
#define TAG_FNUMBER0x829D//光圈,例如 F2.8
#define TAG_EXIF_VERSION0x9000//EXIF 信息版本
#define TAG_DATETIME_ORIGINAL0x9003//照片拍摄时间,例如 2005-10-13 11:09:35
#define TAG_DATATIME_DIGITIZED0x9004//相片被其它图像修改软件修改后的时间,例如 2005-10-13 11:36:35
#define TAG_COMPONCONFIG0x9101//ComponentsConfiguration 色彩空间配置
#define TAG_COMPRESS_BIT0x9202//每像素压缩位数
#define TAG_SHUTTERSPEED0x9201//快门速度,例如 1/30 秒
#define TAG_APERTURE0x9202//光圈值,例如 F2.8
#define TAG_BRIGHTNESS0x9203//亮度
#define TAG_EXPOSURE_BIAS0x9204//曝光补偿,例如 EV0.0
#define TAG_MAXAPERTURE0x9205//最大光圈值,例如 F2.8
#define TAG_SUBJECT_DISTANCE0x9206//拍摄物距离,例如 3.11 米
#define TAG_METERING_MODE0x9207//测光模式,例如矩阵
#define TAG_WHITEBALANCE0x9208//LightSource 白平衡
#define TAG_FLASH0x9209//是否使用闪光灯
#define TAG_FOCALLENGTH0x920A//焦距,例如 7.09mm
#define TAG_USERCOMMENT0x9286//用户注释
#define TAG_MAKE_COMMENT0x927C//厂商注释。这个版本不提供(20 05-10-13)
#define TAG_SUBSECTIME0x9290//SubSecTime
#define TAG_SUBTIME_ORIGINAL0x9291//SubSecTimeOriginal
#define TAG_SUBTIME_DIGITIZED0x9292//SubSecTimeDigitized
#define TAG_FLASHPIXVERSION0x00A0//Flash Pix 版本
#define TAG_COLORSPACE0x01A0//色彩空间,例如 sRGB
#define TAG_PIXEL_XDIMENSION0x02A0//
#define TAG_PIXEL_YDIMENSION0x03A0//
#define TAG_
//EXIFR98
//缩略图
#define TAG_INTEROP_OFFSET0xa005//偏移
#define TAG_FOCALPLANEXRES0xA20E//焦平面X轴分辨率,例如 1024 000/278
#define TAG_FOCALPLANEYRES0xA20F//焦平面X轴分辨率,例如 7680 00/209
#define TAG_FOCALPLANEUNITS0xA210//焦平面分辨率单位
#define TAG_EXIF_IMAGEWIDTH0xA002//EXIF 图像宽度(就是这张 JPG 图像)
#define TAG_EXIF_IMAGELENGTH0xA003//EXIF 图像高度
#define TAG_EXPOSURE_PROGRAM0x8822//
#define TAG_ISO_EQUIVALENT0x8827//
#define TAG_COMPRESSION_LEVEL0x9102//
#define TAG_THUMBNAIL_OFFSET0x0201//缩略图偏移
#define TAG_THUMBNAIL_LENGTH0x0202//缩略图大小
#define TAG_GPS_VERSIONID0x0000//GPS 版本
#define TAG_GPS_LATITUDEREF0x0001//纬度参考,例如南纬
#define TAG_GPS_LATITUDE0x0002//纬度值
#define TAG_GPS_LONGITUDEREF0x0003//经度参考,例如东经
#define TAG_GPS_LONGITUDE0x0004//经度值
#define TAG_GPS_ALTITUDEREF0x0005//海拔高度参考
#define TAG_GPS_ALTITUDE0x0006//海拔
#define TAG_GPS_TIMESTAMP0x0007//时间戳
#define TAG_GPS_SATELLITES0x0008//卫星
#define TAG_GPS_STATUS0x0009//状态
#define TAG_GPS_MEASUREMODE0x000A//
#define TAG_GPS_DOP0x000B//
#define TAG_GPS_SPEEDREF0x000C//
#define TAG_GPS_SPEED0x000D//
#define TAG_GPS_TRACKREF0x000E//
#define TAG_GPS_TRACK0x000F//
#define TAG_GPS_IMGDIRECTIONREF0x0010//
#define TAG_GPS_IMGDIRECTION0x0011//
#define TAG_GPS_MAPDATUM0x0012//
#define TAG_GPS_DESTLATITUDEREF0x0013//
#define TAG_GPS_DESTLATITUDE0x0014//
#define TAG_GPS_DESTLONGITUDEREF0x0015//
#define TAG_GPS_DESTLONGITUDE0x0016//
#define TAG_GPS_DESTBEARINGREF0x0017//
#define TAG_GPS_DESTBEARING0x0018//
#define TAG_GPS_DESTDISTANCEREF0x0019//
#define TAG_GPS_DESTDISTANCE0x001A//
///////////////////////////////////////////////////////////////////// /////
/*--------------------------------------------------------------------------
Get 16 bits motorola order (always) for jpeg header stuff.
--------------------------------------------------------------------------*/
STATIC int EXIF_Get16m(void * Short)
{
return (((unsigned char *)Short)[0] << 8) | ((unsigned char *)Short) [1];
}
/*--------------------------------------------------------------------------
Convert a 16 bit unsigned value from File's native unsigned char orde r
--------------------------------------------------------------------------*/
STATIC int EXIF_Get16u(void * Short)
{
if (m_MotorolaOrder)
{
return (((unsigned char *)Short)[0] << 8) | ((unsigned char *)Sho rt)[1];
}
else
{
return (((unsigned char *)Short)[1] << 8) | ((unsigned char *)Sho rt)[0];
}
}
/*--------------------------------------------------------------------------
Convert a 32 bit signed value from File's native unsigned char order
--------------------------------------------------------------------------*/
STATIC long EXIF_Get32s(void * Long)
{
if (m_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 unsigned char orde r
--------------------------------------------------------------------------*/
STATIC ULONG EXIF_Get32u(void * Long)
{
return (unsigned long)EXIF_Get32s(Long) & 0XFFFFFFFF;
}
/*--------------------------------------------------------------------------
Evaluate number, be it int, rational, or float from directory.
--------------------------------------------------------------------------*/
STATIC double EXIF_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 = EXIF_Get16u(ValuePtr); break; case FMT_ULONG: Value = EXIF_Get32u(ValuePtr); break;
case FMT_URATIONAL:
case FMT_SRATIONAL:
{
int Num,Den;
Num = EXIF_Get32s(ValuePtr);
Den = EXIF_Get32s(4+(char *)ValuePtr);
if (Den == 0)
{
Value = 0;
}
else
{
Value = (double)Num/Den;
}
break;
}
case FMT_SSHORT: Value = (signed short)EXIF_Get16u(ValuePtr); br eak;
case FMT_SLONG: Value = EXIF_Get32s(ValuePtr); br eak;
/* Not sure if this is correct (never seen float used in Exif forma t)
*/
case FMT_SINGLE: Value = (double)*(float *)ValuePtr; break; case FMT_DOUBLE: Value = *(double *)ValuePtr; break; }
return Value;
}
///////////////////////////////////////////////////////////////////// ///////////
///////////////////////////////////////////////////////////////////// /////
/******************************************************************** *
函数声明:
参数:
IN:
OUT:
I/O:
返回值:
功能描述: 处理 JPG 文件中的注释信息
引用:
********************************************************************* /
STATIC void EXIF_Process_COM (CONST UCHAR * 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 w on't fit in our structure.
for (a=2;a ch = Data[a]; if (ch == '\\r' && Data[a+1] == '\\n') continue; // Remove cr follo wed by lf. if ((ch>=0x20) || ch == '\\n' || ch == '\') { Comment[nch++] = (char)ch; } else { Comment[nch++] = '?'; } } Comment[nch] = '\\0'; // Null terminate //if (ShowTags) printf(\"COM marker comment: %s\\n\ strcpy(m_pExifInfo->Comments,Comment); } /******************************************************************** * 函数声明: STATIC BOOL EXIF_ProcessExifDir(...) 参数: IN: CONST UCHAR* DataStart: 数据流的起始位置。这个数值仅仅在函数 EXIF_Decode 中能够改变 CONST DWORD dwFilePointerBeforeReadData: 在读取数据流之前的文件指针位置 UCHAR *DirStart: SECTION 中数据流,去除了前面的 EXIF\\0\\ 0(6)+II(2)+2A00(2)+08000000(6)=14 UCHAR *OffsetBase: 仅仅去除了 EXIFF\\0\\0(6)=6字节 UINT ExifLength: 整个 SECTION 数据流的长度去除 EXIF\\0\\0后的长度==All Length - 6 EXIFINFO * const m_exifinfo: OUT: I/O: UCHAR **const LastExifRefdP: 偏移过后的位置返回值: 功能描述: 引用: ********************************************************************* / STATIC BOOL EXIF_ProcessExifDir(CONST UCHAR* DataStart, CONST DWORD d wFilePointerBeforeReadData, UCHAR *DirStart, UCHAR *OffsetBase, C ONST UINT ExifLength, EXIFINFO * const m_exifinfo, UCHAR ** const LastExifRefdP ) { int de = 0; // int a = 0; // int NumTagEntries = 0; //包含的 TAG 的个数 UINT ThumbnailOffset = 0; //缩略图偏移量 UINT ThumbnailSize = 0; //缩略图的大小 int BytesCount = 0; // UCHAR * TagEntry = 0; //每个 TAG 的入口 int Tag, Format, Components; UCHAR * ValuePtr = 0; //偏移后的位置。因为 TAG 与内容很多时候都不是连续的,而是中间有个偏移量 DWORD OffsetVal = 0; //偏移量 //读取文件中存在 TAG 个数 NumTagEntries = EXIF_Get16u(DirStart); //判断 EXIF 信息的长度是否正确 //下面 DirStart+2 指再去除了 NumTagEntries 所占的 2 个字节 if ((DirStart+2+NumTagEntries*12) > (OffsetBase+ExifLength)) { EXIF_ERR_OUT(\"Illegally sized directory\"); return0; } for (de=0;de //在下面的操作中,所有的数据通通使用 UCHAR* 来表示 TagEntry = DirStart+2+12*de; //TagEntry 的入口点 Tag = EXIF_Get16u(TagEntry); Format = EXIF_Get16u(TagEntry+2); Components = EXIF_Get32u(TagEntry+4); if ((Format-1) >= NUM_FORMATS) { //(-1) catches illegal zero case as unsigned underflows to posi tive large EXIF_ERR_OUT(\"Illegal format code in EXIF dir\"); return0; } BytesCount = Components * m_BytesPerFormat[Format]; if (BytesCount > 4) { OffsetVal = EXIF_Get32u(TagEntry+8); //If its bigger than 4 unsigned chars, the dir entry contains a n offset. if (OffsetVal+BytesCount > ExifLength) { //JPG 文件内容遭到破坏 EXIF_ERR_OUT(\"Illegal pointer offset value in EXIF.\"); return0; } ValuePtr = OffsetBase+OffsetVal; } else { //4 unsigned chars or less and value is in the dir entry itself ValuePtr = TagEntry+8; } if (*LastExifRefdP < ValuePtr+BytesCount) { //当前已经处理的进度 //这样可以再次的检测 JPG 文件的合法性 *LastExifRefdP = ValuePtr+BytesCount; } // 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_DATATIME_DIGITIZED: strncpy(m_exifinfo->DateTimeDigitized, (char*)ValuePtr, 19); break; //用户注释 case TAG_USERCOMMENT: m_exifinfo->UserCOMLength = BytesCount; if(m_exifinfo->dwExifType & ET_MALLOC_USERCOM) { m_exifinfo->UserCOM = malloc(BytesCount); memcpy(m_exifinfo->UserCOM, ValuePtr, BytesCount); /*//Olympus誠cf9 誠cf9 蟎cf9 郳cf9 籠cf9 鶿cf9 籠cf9 醆c f9 診cf9 赲cf9 譢cf9 cf9 蔦cf9 蚛cf9 礬cf9 腬cf9 譢cf9 頫cf9 篭cf9 骪cf9 肻cf9 鎈cf9 蘚cf9 頫cf9 砛cf9 鋅cf9 縗cf9 誠cf9 竆cf 9 馶cf8 0x20禱cf9 鳿cf9 瞈cf9 籠cf9 蔦cf9 荺cf7 '\\0', //下面先将后面的空格替换成 '\\0' 然后再拷贝注释Comment for (a=BytesCount;a>0;) { a--; if (((char*)ValuePtr)[a] == ' ') { ((char*)ValuePtr)[a] = '\\0'; } else { break; } } //将用户注释拷贝到 exifinfo.Comments 中 //首先判断是否是 ASCII 模式(就是注释的前面 5 个字符是 ASCII) //如果是,则取消拷贝最前面的 ASCII 五个字符 if (memcmp(ValuePtr, \"ASCII\ { for (a=5;a<10;a++) { char c; c = ((char*)ValuePtr)[a]; if (c != '\\0' && c != ' ') { strncpy(m_exifinfo->Comments, (char*)ValuePtr+a, MAX_CO MMENT-1); break; } } } else { strncpy(m_exifinfo->Comments, (char*)ValuePtr, MAX_COMMENT-1); }*/ } else { //记录用户注释相对于整个文件起始处的偏移量 m_exifinfo->UserCOM = (CHAR*)(OffsetBase+OffsetVal-DataStart+ dwFilePointerBeforeReadData); //m_exifinfo->UserCOM = (char*)OffsetVal; //偏移 } break; //厂商注释 case TAG_MAKE_COMMENT: m_exifinfo->MakerCOMLength = BytesCount; if(m_exifinfo->dwExifType & ET_MALLOC_MAKERCOM) { m_exifinfo->MakerCOM = malloc(BytesCount); memcpy(m_exifinfo->MakerCOM, ValuePtr, BytesCount); } else { m_exifinfo->MakerCOM = (char*)OffsetVal; //偏移 } break; //光圈 case TAG_FNUMBER: m_exifinfo->ApertureFNumber = (float)EXIF_ConvertAnyFormat(Valu ePtr, 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(EXIF_ConvertAnyForma t(ValuePtr, Format)*log(2)*0.5);//ATTENTION m_exifinfo->ApertureFNumber = (float)(EXIF_ConvertAnyFormat(V aluePtr, Format)*log(2)*0.5); }-*/ break; //Brightness case TAG_BRIGHTNESS: m_exifinfo->Brightness = (float)EXIF_ConvertAnyFormat(ValuePtr, Format); break; //焦距信息(例如 7.09mm) case TAG_FOCALLENGTH: //Nice digital cameras actually save the focal length //as a function of how farthey are zoomed in. m_exifinfo->FocalLength = (float)EXIF_ConvertAnyFormat(ValuePtr, Format); //目标距离(例如 1.11米) 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)EXIF_ConvertAnyFormat(ValuePtr, F ormat); break; //曝光时间(例如 1/30 秒) 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)EXIF_ConvertAnyFormat(ValuePtr, Format); break; //SHUTTERSPEED 快门速度不需要 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(EXIF_ConvertAnyFormat(ValuePtr, Format)*log(2))); }-*/ //FLASH 闪光灯信息不需要 case TAG_FLASH: if ((int)EXIF_ConvertAnyFormat(ValuePtr, Format) & 7) { m_exifinfo->FlashUsed = 1; } else { m_exifinfo->FlashUsed = 0; } break; case TAG_ORIENTATION: m_exifinfo->Orientation = (int)EXIF_ConvertAnyFormat(ValuePtr, Format); if (m_exifinfo->Orientation < 1 || m_exifinfo->Orientation > 8) { EXIF_ERR_OUT(\"Undefined rotation value\"); m_exifinfo->Orientation = 0; } break; //EXIF 图像高度与宽度(例如 1024*768) case TAG_EXIF_IMAGELENGTH: case TAG_EXIF_IMAGEWIDTH: a = (int)EXIF_ConvertAnyFormat(ValuePtr, Format); if (m_ExifImageWidth < a) m_ExifImageWidth = a; break; //焦平面 X 轴分辨率(例如 1024000/278),理论上与 Y 一致case TAG_FOCALPLANEXRES: m_exifinfo->FocalplaneXRes = (float)EXIF_ConvertAnyFormat(Value Ptr, Format); break; //焦平面 Y 轴分辨率(例如 768000/209),理论上与 X 一致 case TAG_FOCALPLANEYRES: m_exifinfo->FocalplaneYRes = (float)EXIF_ConvertAnyFormat(Value Ptr, Format); break; case TAG_RESOLUTIONUNIT: switch((int)EXIF_ConvertAnyFormat(ValuePtr, Format)) { case1: m_exifinfo->ResolutionUnit = 1.0f; break; // 1 inch case2: m_exifinfo->ResolutionUnit = 1.0f; break; // case3: m_exifinfo->ResolutionUnit = 0.3937007874f; break; // 1 centimeter case4: m_exifinfo->ResolutionUnit = 0.0393*******f; break; // 1 millimeter case5: m_exifinfo->ResolutionUnit = 0.00003937007874f; // 1 micrometer } break; //焦平面分辨率单位(例如米) case TAG_FOCALPLANEUNITS: switch((int)EXIF_ConvertAnyFormat(ValuePtr, Format)) { case1: m_exifinfo->FocalplaneUnits = 1.0f; break; // 1 inch case2: m_exifinfo->FocalplaneUnits = 1.0f; break; // case3: m_exifinfo->FocalplaneUnits = 0.3937007874f; break; // 1 centimeter case4: m_exifinfo->FocalplaneUnits = 0.0393*******f; break; // 1 millimeter case5: m_exifinfo->FocalplaneUnits = 0.00003937007874f;break; // 1 micrometer// } break; //曝光补偿信息 case TAG_EXPOSURE_BIAS: m_exifinfo->ExposureBias = (float) EXIF_ConvertAnyFormat(ValueP tr, Format); break; //白平衡 case TAG_WHITEBALANCE: m_exifinfo->Whitebalance = (int)EXIF_ConvertAnyFormat(ValuePtr, Format); break; case TAG_METERING_MODE: m_exifinfo->MeteringMode = (int)EXIF_ConvertAnyFormat(ValuePtr, Format); break; case TAG_EXPOSURE_PROGRAM: m_exifinfo->ExposureProgram = (int)EXIF_ConvertAnyFormat(ValueP tr, Format); break; case TAG_ISO_EQUIVALENT: m_exifinfo->ISOequivalent = (int)EXIF_ConvertAnyFormat(ValuePtr, Format); if ( m_exifinfo->ISOequivalent < 50 ) m_exifinfo->ISOequivalent *= 200; break; case TAG_COMPRESSION_LEVEL: m_exifinfo->CompressionLevel = (int)EXIF_ConvertAnyFormat(Value Ptr, Format); break; //X 轴分辨率 case TAG_XRESOLUTION: m_exifinfo->Xresolution = (float)EXIF_ConvertAnyFormat(ValuePtr, Format); break; //Y 轴分辨率 case TAG_YRESOLUTION: m_exifinfo->Yresolution = (float)EXIF_ConvertAnyFormat(ValuePtr, Format); break; //缩略图偏移量 case TAG_THUMBNAIL_OFFSET: ThumbnailOffset = (unsigned)EXIF_ConvertAnyFormat(ValuePtr, For mat); break; //缩略图的大小 case TAG_THUMBNAIL_LENGTH: ThumbnailSize = (unsigned)EXIF_ConvertAnyFormat(ValuePtr, For mat); break; } //end switch(Tag) //EXIF 信息偏移 // if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET) { UCHAR * SubdirStart; SubdirStart = OffsetBase + EXIF_Get32u(ValuePtr); if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength) { EXIF_ERR_OUT(\"Illegal subdirectory link\"); return0; } EXIF_ProcessExifDir(DataStart, dwFilePointerBeforeReadData, Sub dirStart, OffsetBase, ExifLength, m_exifinfo, LastExifRefdP); continue; } } //end for {for (de=0;de //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! UCHAR * SubdirStart; unsigned Offset; Offset = EXIF_Get16u(DirStart+2+12*NumTagEntries); if (Offset) { SubdirStart = OffsetBase + Offset; if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength) { EXIF_ERR_OUT(\"Illegal subdirectory link\"); return0; } EXIF_ProcessExifDir(DataStart, dwFilePointerBeforeReadData, Sub dirStart, OffsetBase, ExifLength, m_exifinfo, LastExifRefdP); } } if (ThumbnailSize && ThumbnailOffset) { //如果文件中存在缩略图,那么将缩略图的数据保存 //注意:这里仅仅负责 malloc,调用者需要自己 free if (ThumbnailSize + ThumbnailOffset <= ExifLength) { //将缩略图的数据全部拷贝到一块新开辟的内存 if(m_exifinfo->dwExifType&ET_MALLOC_THUMBNAIL) { UCHAR *pThumbnailData = OffsetBase + ThumbnailOffset; DWORD dw = pThumbnailData-DataStart+dwFilePointerBeforeReadDa ta; m_exifinfo->ThumbnailPointer = (UCHAR*)malloc(ThumbnailSize); memcpy(m_exifinfo->ThumbnailPointer, pThumbnailData, Thumbnai lSize); } else { m_exifinfo->ThumbnailPointer = (UCHAR*)(OffsetBase+ThumbnailO ffset-DataStart+dwFilePointerBeforeReadData); } m_exifinfo->ThumbnailSize = ThumbnailSize; } } return TRUE; } /******************************************************************** * 函数声明: STATIC BOOL EXIF_process_EXIF(UCHAR * CharBuf, UINT lengt h) 参数: IN: CONST UCHAR* DataStart: 数据流的起始位置。这个数值仅仅在函数 EXIF_Decode 中能够改变 CONST DWORD dwFilePointerBeforeReadData: 在读取数据流之前的文件指针位置 UCHAR * CharBuf: 这个 SECTION 数据内容。注意:前面已经去掉了包含长度的2个字符 CONST UINT length: 这个 SECTION 数据流的长度返回值: 功能描述: 处理某个 SECTION 中的 EXIF 信息。 成功返回TRUE表示EXIF信息存在且正确,失败返回FALSE 引用: ********************************************************************* / STATIC BOOL EXIF_process_EXIF(CONST UCHAR *DataStart, CONST DWORD dw FilePointerBeforeReadData, UCHAR *CharBuf, CONST UINT length) { int FirstOffset = 0; UCHAR *LastExifRefd = 0; m_pExifInfo->FlashUsed = 0; m_pExifInfo->Comments[0] = '\\0'; m_ExifImageWidth = 0; //检查 EXIF 头是否正确 { static const unsigned char ExifHeader[] = \"Exif\\0\\0\"; if (memcmp(CharBuf+0, ExifHeader,6)) { EXIF_ERR_OUT(\"Incorrect Exif header\"); return0; } } //判断内存中数据的排列是按照 Intel 还是按照 Motorola CPU 排列的 if (memcmp(CharBuf+6,\"II\ { m_MotorolaOrder = 0; // } else if (memcmp(CharBuf+6,\"MM\ { m_MotorolaOrder = 1; // } else { EXIF_ERR_OUT(\"Invalid Exif alignment marker.\"); return0; } //检查下面 2 个字节是否是 0x2A00 if (EXIF_Get16u(CharBuf+8) != 0x2A) { EXIF_ERR_OUT(\"Invalid Exif start (1)\"); return0; } //判断下面的 0th IFD Offset 是否是 0x08000000 FirstOffset = EXIF_Get32u(CharBuf+10); if (FirstOffset < 8 || FirstOffset > 16) { EXIF_ERR_OUT(\"Suspicious offset of first IFD value\"); return0; } LastExifRefd = CharBuf; //开始处理 EXIF 信息 if (!EXIF_ProcessExifDir(DataStart, dwFilePointerBeforeReadData, CharBuf+14, CharBuf+6, length-6, m_pExifInfo, &LastExifRefd)) { return0; } // This is how far the interesting (non thumbnail) part of the exif went. // int ExifSettingsLength = LastExifRefd - CharBuf; // 计算 CCD 宽度(单位:毫米) if (m_pExifInfo->FocalplaneXRes != 0) { m_pExifInfo->CCDWidth = (float)(m_ExifImageWidth * m_pExifInfo->F ocalplaneUnits / m_pExifInfo->FocalplaneXRes); } return1; } STATIC VOID EXIF_process_SOFn (CONST UCHAR * Data, int marker) { int data_precision, num_components; data_precision = Data[2]; m_pExifInfo->Height = EXIF_Get16m((void*)(Data+3)); m_pExifInfo->Width = EXIF_Get16m((void*)(Data+5)); num_components = Data[7]; if (num_components == 3) { m_pExifInfo->IsColor = 1; } else { m_pExifInfo->IsColor = 0; } m_pExifInfo->Process = marker; //if (ShowTags) printf(\"JPEG image is %uw * %uh, %d color component s, %d bits per sample\\n\ // ImageInfo.Width, ImageInfo.Height, num_components, data_precision); } STATIC int EXIF_Decode(HANDLE hFile) { int a=0, b=0; int nHaveCom = 0; //是否存在注释,并且保存注释字符串的长度 int nSectionsRead = 0; //已经读取 SECTION 的个数 DWORD dwFileRead = 0; //使用 ReadFile 读取文件时,读取的字节数 DWORD dwFilePointerBeforeReadData = 0; //在读取数据流之前,文件指针的位置 Section_t Sections[MAX_SECTIONS]; //JPG 文件中 SECTIONS int nSectionLength=0; //SECTION(APP) 长度 int marker = 0; // int ll=0,lh=0, got=0; // UCHAR *Data = 0; // //读入 JPG 第1, 2个字节,判断是否是 0xFF,M_SOI ReadFile(hFile, &a, 1, &dwFileRead, NULL); if(dwFileRead != 1) { EXIF_ERR_OUT(\"Unexpect File End\"); return -1; } ReadFile(hFile, &b, 1, &dwFileRead, NULL); if(dwFileRead != 1) { EXIF_ERR_OUT(\"Unexpect File End\"); return -1; } //判断该文件是否是 EXIF 文件 //EXIF 文件的起始 2 字节必定是 FF D8 if (a != 0xFF || b != M_SOI) { EXIF_ERR_OUT(\"File Format Error\"); return -1; } //使用一个循环,读取 JPG 文件中的 SECTION //第一个 SECTION 肯定是 APP1,而APP1起始的Marker肯定为 FFE1 for(;;) { if (nSectionsRead >= MAX_SECTIONS) { EXIF_ERR_OUT(\"Too many sections in this jpg File\"); return -1; } //查找 JPG 文件填充字符,接下来的7个字符必须有一个不是 0xFF for (a=0;a<7;a++) { ReadFile(hFile, &marker, 1, &dwFileRead, NULL); if(dwFileRead != 1) { EXIF_ERR_OUT(\"Unexpect File End\"); return -1; } if (marker != 0xFF) break; if (a >= 6) { EXIF_ERR_OUT(\"Too many padding unsigned chars\"); return -1; } } #ifdef _DEBUG if(nSectionsRead==0) //是 APP 1 { ASSERT(marker == M_APP1); } #endif Sections[nSectionsRead].Type = marker; //记录读取流数据之前的文件指针位置 dwFilePointerBeforeReadData = SetFilePointer(hFile, 0, NULL, FILE _CURRENT); //读取这个 SECTION 的长度 ReadFile(hFile, &lh, 1, &dwFileRead, NULL); ReadFile(hFile, &ll, 1, &dwFileRead, NULL); nSectionLength = (lh << 8) | ll; //EXIF 文件高字节在前,低字节在后,不能读取一个WORD类型 if (nSectionLength < 2) { EXIF_ERR_OUT(\"Invalid Marker\"); return -1; } Data = (UCHAR *)malloc(nSectionLength); if (Data == NULL) { EXIF_ERR_OUT(\"Could not allocate memory!\"); return -1; } Sections[nSectionsRead].Data = Data; // Store first two pre-read unsigned chars. Data[0] = (UCHAR)lh; Data[1] = (UCHAR)ll; ReadFile(hFile, Data+2, nSectionLength-2, &dwFileRead, NULL); if(dwFileRead !=(DWORD)(nSectionLength-2)) { EXIF_ERR_OUT(\"Premature end of file?\"); return -1; } dwFileRead = SetFilePointer(hFile, 0, NULL, FILE_CURRENT); //得到当前文件的指针位置 nSectionsRead += 1; switch(marker) { case M_SOS: //到了数据区 return1; case M_EOI: //End Of Image EXIF_ERR_OUT(\"No image in this jpeg file\"); return -1; case M_COM: //注释区 if (nHaveCom) { // Discard this section. free(Sections[--nSectionsRead].Data); Sections[nSectionsRead].Data=0; } else { EXIF_Process_COM(Data, nSectionLength); nHaveCom = 1; } case M_JFIF: //标准的 JPG 文件通常都有 TAG,并且处于 M_APP0 中 //EXIF 图像使用 exif 标记(在 M_APP1 中)来取代这个 //但是有些软件(例如 ACDSee)在修改 JPG 文件后会同时保留这 2 个T AG //我们在这里不需要 M_JFIF 信息,直接跳过去即可 //如果是重写文件,仅仅需要把 JFIF 信息复制到新的JPG文件头 free(Sections[--nSectionsRead].Data); Sections[nSectionsRead].Data=0; break; case M_EXIF: //EXIF 信息 TAG if (memcmp(Data+2, \"Exif\ { m_pExifInfo->IsExif = EXIF_process_EXIF(Data, dwFilePointerBe foreReadData, (unsigned char *)Data+2, nSectionLength); } else { // Discard this section. free(Sections[--nSectionsRead].Data); Sections[nSectionsRead].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: EXIF_process_SOFn(Data, marker); break; default: // Skip any other sections. //if (ShowTags) printf(\"Jpeg section marker 0x%02x size %d\\n\arker, nSectionLength); break; } } return1; } /******************************************************************** * 函数声明: 参数: IN: LPCTSTR pszJpgFileName: JPG 文件全路径名 OUT: I/O: EXIFINFO* pExifInfo: 保存了 EXIF 信息的结构体返回值: >=0表示成功,<0读取失败 功能描述: 读取并返回 JPG 文件中的 EXIF 信息 引用: 外部调用者 注意: ********************************************************************* / int EXIF_Read(LPCTSTR pszJpgFileName, EXIFINFO* pExifInfo) { int nReturn = -1; HANDLE hFile = INVALID_HANDLE_VALUE; if(pExifInfo == 0) { EXIF_ERR_OUT(\"Parameter incorreted! pExifInfo must not be NULL! \"); return -1; } hFile = CreateFile(pszJpgFileName, //LPCTSTR lpcszFileName GENERIC_READ, //DWORD dwAccess FILE_SHARE_READ, //DWORD dwShareMode NULL, //LPSECURITY_ATTRIBUTES lpSecur ityAttributes OPEN_EXISTING, //DWORD dwCreate.打开文件,如果不存在则失败 FILE_ATTRIBUTE_NORMAL, //DWORD dwFlagsAndAttributes NULL//HANDLE hTemplateFile ); //打开 JPG 文件失败 if(hFile == INVALID_HANDLE_VALUE) { EXIF_ERR_OUT(\"JPG File Not Found!\"); return -1; } //将文件指针移到最前 SetFilePointer(hFile, 0, NULL, FILE_BEGIN); //开始处理 JPG 文件 pExifInfo->ThumbnailPointer = NULL; pExifInfo->ThumbnailSize = 0; pExifInfo->IsExif = FALSE; m_pExifInfo = pExifInfo; nReturn = EXIF_Decode(hFile); if(nReturn>=0 && (m_pExifInfo->dwExifType&ET_NOT_CLOSE_File) ) { m_pExifInfo->hJpgFileHandle = hFile; } else { CloseHandle(hFile); } return nReturn; } /******************************************************************** * 函数声明: int EXIF_AddUserComments(LPCTSTR pszJpgFileName, LPCTSTR pszUserComments, DWORD dwCommentLength, COMMENT_TYPE nCommentType) 参数: IN: LPCTSTR pszJpgFileName: JPG 文件全路径名 LPCTSTR pszUserComments: 需要写入的注释 DWORD dwCommentLength: 需要写入的注释的长度 COMMENT_TYPE nCommentType: 注释写入时的类型,例如 ASCII, UNICODE, JIS 等 OUT: I/O: 返回值: 成功返回一个>0的数值表示写入的注释长度,失败返回<0 功能描述: 将指定的用户注释写入到 JPG 文件中 引用: ********************************************************************* / int EXIF_AddUserComments(LPCTSTR pszJpgFileName, LPCTSTR pszUserComme nts, DWORD dwCommentLength, COMMENT_TYPE ctCommentType) { int nReturn = -1; EXIFINFO exifinfo = {0}; HANDLE hFile = INVALID_HANDLE_VALUE; DWORD dwWriteBytes = 0; TCHAR pszCommentType[8] = {0}; switch(ctCommentType) { case CT_ASCII: strcpy(pszCommentType, \"ASCII\"); break; case CT_UNDEFINE: strcpy(pszCommentType, \"UNICODE\"); break; case CT_JIS: strcpy(pszCommentType, \"JIS\"); break; } if(pszUserComments == 0) { EXIF_ERR_OUT(\"Parameter incorreted! pszUserComments must not be N ULL!\"); return -1; } if(dwWriteBytes > strlen(pszUserComments)) { EXIF_ERR_OUT(\"dwWriteBytes must be bigger or equal than the lengt h of user comments!\"); return -1; } hFile = CreateFile(pszJpgFileName, //LPCTSTR lpcszFileName GENERIC_WRITE|GENERIC_READ, //DWORD dwAccess FILE_SHARE_WRITE|FILE_SHARE_READ, //DWORD dwShareMode NULL, //LPSECURITY_ATTRIBUTES lpSecur ityAttributes OPEN_ALWAYS, //DWORD dwCreate.打开文件,如果不存在则创建 FILE_ATTRIBUTE_NORMAL, //DWORD dwFlagsAndAttributes NULL//HANDLE hTemplateFile ); //打开 JPG 文件失败 if(hFile == INVALID_HANDLE_VALUE) { EXIF_ERR_OUT(\"JPG File Not Found!\"); return -1; } //将文件指针移到最前 SetFilePointer(hFile, 0, NULL, FILE_BEGIN); //开始处理 JPG 文件 exifinfo.dwExifType |= ET_NOT_CLOSE_FILE; exifinfo.ThumbnailPointer = NULL; exifinfo.ThumbnailSize = 0; exifinfo.UserCOM = 0; exifinfo.UserCOMLength = 0; m_pExifInfo = &exifinfo; nReturn = EXIF_Decode(hFile); if(nReturn>=0 && (m_pExifInfo->dwExifType&ET_NOT_CLOSE_FILE) ) { m_pExifInfo->hJpgFileHandle = hFile; } else { CloseHandle(hFile); } if(nReturn<0) { return nReturn; } //如果这个 JPG 文件中没有内存缓冲区 if(m_pExifInfo->UserCOMLength<=8) { EXIF_ERR_OUT(\"This JPG file not include user comments buffer area! \"); CloseHandle(hFile); return -1; } SetFilePointer(hFile, 0, 0, FILE_BEGIN); SetFilePointer(hFile, (DWORD)m_pExifInfo->UserCOM, 0, FILE_BEGIN); //下面写入用户的注释 //写入编码方式 WriteFile(hFile, pszCommentType, 8, &dwWriteBytes, NULL); m_pExifInfo->UserCOMLength -= 8; //前面有 8 个字节的编码方式 if(m_pExifInfo->UserCOMLength>dwWriteBytes) { WriteFile(hFile, pszUserComments, dwCommentLength+1, &dwWriteByte s, NULL); nReturn = (int)dwWriteBytes; } else { WriteFile(hFile, pszUserComments, m_pExifInfo->UserCOMLength+1, & dwWriteBytes, NULL); nReturn = (int)dwWriteBytes; } //再写入 \\0 //WriteFile(hFile, '\\0', 1, &dwWriteBytes, NULL); return nReturn; }
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- 99spj.com 版权所有 湘ICP备2022005869号-5
违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务