• GOEXIF读取和写入EXIF信息


    最新版本的gexif,直接基于gdi+实现了exif信息的读取和写入,代码更清晰。

    /*
     * File:	gexif.h
     * Purpose:	cpp EXIF reader
     * 3/2/2017 <jsxyhelu@foxmail.com>
     * 基于GDI+的EXIF读写类
     */
    //2017年2月3日 jsxyhelu 在原有cexif的基础上重构
    #pragma once
    
    
    #include <stdlib.h>
    #include <memory.h>
    #include <string.h>
    #include <stdio.h>
    #include <math.h>
    #include <windows.h>
    #include <gdiplus.h>
    #include <stdio.h>
    #pragma comment(lib, "gdiplus.lib")   
    
    class Gexif
    {
    public:
    	Gexif(void);
    	~Gexif(void);
    	int GetEncoderClsid(const WCHAR* format, CLSID* pClsid);
    	Gdiplus::Bitmap* LoadBitmapFromMemory(const void* memory, DWORD size);
    	Gdiplus::Bitmap* LoadBitmapFromFile(const TCHAR* file_name);
    	int WriteExif2Image(CString strFileName, int PropertyTag, CString propertyValue);
    	CString ReadExifFromImage(CString strFileName, int PropertyTag);
    	float GetXresoutionFromImage(CString strFileName);
    };
    
    /*
     * File:	gexif.h
     * Purpose:	cpp EXIF reader
     * 3/2/2017 <jsxyhelu@foxmail.com>
     * 基于GDI+的EXIF读写类
     */
    
    #include "stdafx.h"
    #include "Gexif.h"
    using namespace Gdiplus;
    
    Gexif::Gexif(void)
    {
    }
    
    
    Gexif::~Gexif(void)
    {
    }
    
    
    int Gexif::GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
    {
    	return 0;
    }
    
    // 从内存加载图片,失败返回NULL
    Gdiplus::Bitmap* Gexif::LoadBitmapFromMemory(const void* memory, DWORD size)
    {
    	Bitmap* bmp = NULL;
    	IStream* stream = NULL;
    	if (CreateStreamOnHGlobal(NULL, TRUE, &stream) == S_OK)
    	{
    		ULARGE_INTEGER uli;
    		uli.QuadPart = size;
    		stream->SetSize(uli);
    		if (stream->Write(memory, size, NULL) == S_OK)
    			bmp = new Bitmap(stream);
    		stream->Release();
    	}
    	return bmp;
    }
    
    // 从文件加载图片,不独占文件,失败返回NULL
    Gdiplus::Bitmap* Gexif::LoadBitmapFromFile(const TCHAR* file_name)
    {
    	Bitmap* bmp = NULL;
    	HANDLE file_handle = CreateFile(file_name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    	if (file_handle != INVALID_HANDLE_VALUE)
    	{
    		DWORD temp = 0;
    		DWORD file_size = GetFileSize(file_handle, &temp);
    		if (file_size && !temp)  // 不处理大于4G的文件
    		{
    			// 将图片文件读到内存后,再从内存创建Bitmap
    			unsigned char* buffer = new unsigned char[file_size];
    			if (ReadFile(file_handle, buffer, file_size, &temp, NULL))
    				bmp = LoadBitmapFromMemory(buffer, temp);
    			delete [] buffer;
    		}
    		CloseHandle(file_handle);
    	}
    	return bmp;
    }
    
    //写入exif信息到图片中去,目前只支持jpeg格式
    //返回 0为正常 -1为异常
    //各种tchar wchar cstring的转换,技巧较多.
    int Gexif::WriteExif2Image(CString strFileName, int PropertyTag, CString pValue)
    {
    	GdiplusStartupInput gdiplusStartupInput;
    	ULONG_PTR gdiplusToken;
    	GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    	Status stat;
    	CLSID  clsid;
    
    	char* propertyValue = (char*)pValue.GetBuffer(pValue.GetLength());
    	Bitmap* bitmap = LoadBitmapFromFile(strFileName);
    	PropertyItem* propertyItem = new PropertyItem;
    	GetEncoderClsid(L"image/jpeg", &clsid);
    
    	propertyItem->id = PropertyTag;//给Tag赋值
    	propertyItem->length = 255;  
    	propertyItem->type = PropertyTagTypeASCII; 
    	propertyItem->value = propertyValue;
    	bitmap->SetPropertyItem(propertyItem);
    
    
    	WCHAR wcharbuf[100];
    	CStringW strWide = CT2W(strFileName);
    	wcscpy(wcharbuf,strWide);
    	stat = bitmap->Save(wcharbuf, &clsid, NULL);
    
    	if(stat != Ok)
    		return -1;
    	delete propertyItem;
    	delete bitmap;
    	GdiplusShutdown(gdiplusToken);
    	return 0;
    }
    
    //使用基于GDI+,获取Exif信息
    //这里种类很多的,目前只是实现了copytright
    CString Gexif::ReadExifFromImage(CString strFileName, int PropertyTag)
    {
    	CString strRet;//用于返回的str
    
    	GdiplusStartupInput gdiplusStartupInput;
    	ULONG_PTR gdiplusToken;
    	GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    	Status stat;
    	CLSID  clsid;
    
    	//打开图片
    	Bitmap* bitmap = LoadBitmapFromFile(strFileName);
    	int icount = bitmap->GetPropertyCount();
    	PROPID* propIDs = new PROPID[icount];
    	bitmap->GetPropertyIdList(icount,propIDs);
    	UINT size=0;
    	UINT type = 0;
    	Gdiplus::PropertyItem* propertyItem = NULL;
    	//定位到这个PropertyTag
    	/*
    	#define PropertyTagTypeByte        1
    	#define PropertyTagTypeASCII       2
    	#define PropertyTagTypeShort       3
    	#define PropertyTagTypeLong        4
    	#define PropertyTagTypeRational    5
    	#define PropertyTagTypeUndefined   7
    	#define PropertyTagTypeSLONG       9
    	#define PropertyTagTypeSRational  10
    	*/
    	for(UINT j=0;j < icount; ++j)
    	{
    		 if (PropertyTag == propIDs[j])
    		 {
    			 size= bitmap->GetPropertyItemSize(propIDs[j]);
    			 propertyItem= (Gdiplus::PropertyItem*)malloc(size);
    			 bitmap->GetPropertyItem(propIDs[j], size, propertyItem);
    			 type = propertyItem->type;
    			 char cbuf[255];
    			 switch (type)
    			 {
    			 case  PropertyTagTypeASCII  :
    				 strcpy(cbuf,(char*)propertyItem->value);
    				 strRet = cbuf;
    				 return strRet;
    			 default:
    				 return _T("empty");
    				 break;
    			 }
    			
    		 }
    	} 
    	
    }
    
    //获得x方向的分辨率
    float Gexif::GetXresoutionFromImage(CString strFileName)
    {
    	GdiplusStartupInput gdiplusStartupInput;
    	ULONG_PTR gdiplusToken;
    	GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    	Status stat;
    	CLSID  clsid;
    	Bitmap* bitmap = LoadBitmapFromFile(strFileName);
    	float fret = bitmap->GetHorizontalResolution();
    	return fret;
    }
    

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    能够读取和写入EXIF信息,使得图像处理程序在不改变原有图片内容、不添加多余文件的情况下可以保存参数信息,非常有价值。

    这方面中英文资料比较少,经过较长时间的研究和集成,我在网络相关资料的基础上完成了GOEXIF,实现了MFC上的直接读取和写入。
    读取
    EXIFINFO m_exifinfo;
    FILE* hFile=fopen(FilePathName.c_str(),"rb");
    if (hFile){
    memset(&m_exifinfo,0,sizeof(EXIFINFO));
    Cexif exif(&m_exifinfo);
    exif.DecodeExif(hFile);
    fclose(hFile);
    写入
    Cexif ceif;
    sprintf_s(cbuf2,"123");
    int iret = ceif.WriteExif2Image(FilePathName.c_str(),PropertyTagCopyright,cbuf2);
    if(iret == 0)
    AfxMessageBox("写入EXIF信息成功!");
    else
    AfxMessageBox("写入EXIF信息失败!");
    已经能够投入实际使用,但是由于是多种资料融合,代码还是需要重构。我会结合自己工作来做,如果哪位有兴趣继续开发,这应该是一个很好的基础。
     
    /*
     * File:    exif.h
     * Purpose:    cpp EXIF reader
     * 16/Mar/2003 <ing.davide.pizzolato@libero.it>
     * based on jhead-1.8 by Matthias Wandel <mwandel(at)rim(dot)net>
     */
    //2016年11月25日 jsxyhelu 修改添加版权信息
    //2017年1月21日  jsxyhelu 添加写入exif的功能
     
    //使用例子 (基于MFC)
    /*读取
    EXIFINFO m_exifinfo;
    FILE* hFile=fopen(FilePathName.c_str(),"rb");
    if (hFile){
    memset(&m_exifinfo,0,sizeof(EXIFINFO));
    Cexif exif(&m_exifinfo);
    exif.DecodeExif(hFile);
    fclose(hFile);
    写入
    Cexif ceif;
    sprintf_s(cbuf2,"123");
    int iret = ceif.WriteExif2Image(FilePathName.c_str(),PropertyTagCopyright,cbuf2);
    if(iret == 0)
    AfxMessageBox("写入EXIF信息成功!");
    else
    AfxMessageBox("写入EXIF信息失败!");
    */
    #if !defined(__exif_h)
    #define __exif_h
     
    #include <stdlib.h>
    #include <memory.h>
    #include <string.h>
    #include <stdio.h>
    #include <math.h>
    #include <windows.h>
    #include <gdiplus.h>
    #include <stdio.h>
    #pragma comment(lib"gdiplus.lib")   
     
    #define MAX_COMMENT 65535
    #define MAX_SECTIONS 20
     
     
    typedef struct tag_ExifInfo {
        char  Version      [5];
        char  CameraMake   [32];
        char  CameraModel  [40];
        char  DateTime     [20];
        char  CopyRight    [MAX_COMMENT];
        int   HeightWidth;
        int   Orientation;
        int   IsColor;
        int   Process;
        int   FlashUsed;
        float FocalLength;
        float ExposureTime;
        float ApertureFNumber;
        float Distance;
        float CCDWidth;
        float ExposureBias;
        int   Whitebalance;
        int   MeteringMode;
        int   ExposureProgram;
        int   ISOequivalent;
        int   CompressionLevel;
        float FocalplaneXRes;
        float FocalplaneYRes;
        float FocalplaneUnits;
        float Xresolution;
        float Yresolution;
        float ResolutionUnit;
        float Brightness;
        char  Comments[MAX_COMMENT];
     
        unsigned char * ThumbnailPointer;  /* Pointer at the thumbnail */
        unsigned ThumbnailSize;     /* Size of thumbnail. */
     
        bool  IsExif;
    EXIFINFO;
     
    //--------------------------------------------------------------------------
    // JPEG markers consist of one or more 0xFF unsigned chars, followed by a marker
    // code unsigned char (which is not an FF).  Here are the marker codes of interest
    // in this program.  (See jdmarker.c for a more complete list.)
    //--------------------------------------------------------------------------
     
    #define M_SOF0  0xC0            // Start Of Frame N
    #define M_SOF1  0xC1            // N indicates which compression process
    #define M_SOF2  0xC2            // Only SOF0-SOF2 are now in common use
    #define M_SOF3  0xC3
    #define M_SOF5  0xC5            // NB: codes C4 and CC are NOT SOF markers
    #define M_SOF6  0xC6
    #define M_SOF7  0xC7
    #define M_SOF9  0xC9
    #define M_SOF10 0xCA
    #define M_SOF11 0xCB
    #define M_SOF13 0xCD
    #define M_SOF14 0xCE
    #define M_SOF15 0xCF
    #define M_SOI   0xD8            // Start Of Image (beginning of datastream)
    #define M_EOI   0xD9            // End Of Image (end of datastream)
    #define M_SOS   0xDA            // Start Of Scan (begins compressed data)
    #define M_JFIF  0xE0            // Jfif marker
    #define M_EXIF  0xE1            // Exif marker
    #define M_COM   0xFE            // COMment 
     
    class Cexif
    {
    typedef struct tag_Section_t{
        unsigned char*    Data;
        int      Type;
        unsigned Size;
    Section_t;
     
    public:
        EXIFINFOm_exifinfo;
        char m_szLastError[256];
        Cexif(EXIFINFOinfo = NULL);
        ~Cexif();
        bool DecodeExif(FILEhFile);
    protected:
        bool process_EXIF(unsigned char * CharBufunsigned int length);
        void process_COM (const unsigned char * Dataint length);
        void process_SOFn (const unsigned char * Dataint marker);
        int Get16u(void * Short);
        int Get16m(void * Short);
        long Get32s(void * Long);
        unsigned long Get32u(void * Long);
        double ConvertAnyFormat(void * ValuePtrint Format);
        bool ProcessExifDir(unsigned char * DirStartunsigned char * OffsetBaseunsigned ExifLength,
                               EXIFINFO * const pInfounsigned char ** const LastExifRefdP);
        int ExifImageWidth;
        int MotorolaOrder;
        Section_t Sections[MAX_SECTIONS];
        int SectionsRead;
        bool freeinfo;
    public:
        int GetEncoderClsid(const WCHARformatCLSIDpClsid);
        Gdiplus::BitmapLoadBitmapFromMemory(const voidmemoryDWORD size);
        Gdiplus::BitmapLoadBitmapFromFile(const TCHARfile_name);
        int WriteExif2Image(CString strFileNameint PropertyTagCString propertyValue);
    };
     
    #endif
     
    /*
     * File:    exif.h
     * Purpose:    cpp EXIF reader
     * 16/Mar/2003 <ing.davide.pizzolato@libero.it>
     * based on jhead-1.8 by Matthias Wandel <mwandel(at)rim(dot)net>
     */
    //2016年11月25日 jsxyhelu 修改添加版权信息
    //2017年1月21日  jsxyhelu 添加写入exif的功能
    #include "stdafx.h"
    #include "Exif.h"
    using namespace Gdiplus;
    ////////////////////////////////////////////////////////////////////////////////
    Cexif::Cexif(EXIFINFOinfo)
    {
        if (info) {
            m_exifinfo = info;
            freeinfo = false;
        } else {
            m_exifinfo = new EXIFINFO;
            memset(m_exifinfo,0,sizeof(EXIFINFO));
            freeinfo = true;
        }
     
        m_szLastError[0]='';
        ExifImageWidth = MotorolaOrder = 0;
        SectionsRead=0;
        memset(&Sections, 0, MAX_SECTIONS * sizeof(Section_t));
    }
    ////////////////////////////////////////////////////////////////////////////////
    Cexif::~Cexif()
    {
        for(int i=0;i<MAX_SECTIONS;i++) if(Sections[i].Datafree(Sections[i].Data);
        if (freeinfodelete m_exifinfo;
    }
    ////////////////////////////////////////////////////////////////////////////////
    bool Cexif::DecodeExif(FILE * hFile)
    {
        int a;
        int HaveCom = 0;
     
        a = fgetc(hFile);
     
        if (a != 0xff || fgetc(hFile) != M_SOI){
            return 0;
        }
     
        for(;;){
            int itemlen;
            int marker = 0;
            int ll,lhgot;
            unsigned char * Data;
     
            if (SectionsRead >= MAX_SECTIONS){
                strcpy(m_szLastError,"Too many sections in jpg file");
                return 0;
            }
     
            for (a=0;a<7;a++){
                marker = fgetc(hFile);
                if (marker != 0xff) break;
     
                if (a >= 6){
                    printf("too many padding unsigned chars ");
                    return 0;
                }
            }
     
            if (marker == 0xff){
                // 0xff is legal padding, but if we get that many, something's wrong.
                strcpy(m_szLastError,"too many padding unsigned chars!");
                return 0;
            }
     
            Sections[SectionsRead].Type = marker;
     
            // Read the length of the section.
            lh = fgetc(hFile);
            ll = fgetc(hFile);
     
            itemlen = (lh << 8) | ll;
     
            if (itemlen < 2){
                strcpy(m_szLastError,"invalid marker");
                return 0;
            }
     
            Sections[SectionsRead].Size = itemlen;
     
            Data = (unsigned char *)malloc(itemlen);
            if (Data == NULL){
                strcpy(m_szLastError,"Could not allocate memory");
                return 0;
            }
            Sections[SectionsRead].Data = Data;
     
            // Store first two pre-read unsigned chars.
            Data[0] = (unsigned char)lh;
            Data[1] = (unsigned char)ll;
     
            got = fread(Data+2, 1, itemlen-2,hFile); // Read the whole section.
            if (got != itemlen-2){
                strcpy(m_szLastError,"Premature end of file?");
                return 0;
            }
            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 (ReadMode & READ_IMAGE){
                        int cp, ep, size;
                        // Determine how much file is left.
                        cp = ftell(infile);
                        fseek(infile, 0, SEEK_END);
                        ep = ftell(infile);
                        fseek(infile, cp, SEEK_SET);
     
                        size = ep-cp;
                        Data = (uchar *)malloc(size);
                        if (Data == NULL){
                            strcpy(m_szLastError,"could not allocate data for entire image");
                            return 0;
                        }
     
                        got = fread(Data, 1, size, infile);
                        if (got != size){
                            strcpy(m_szLastError,"could not read the rest of the image");
                            return 0;
                        }
     
                        Sections[SectionsRead].Data = Data;
                        Sections[SectionsRead].Size = size;
                        Sections[SectionsRead].Type = PSEUDO_IMAGE_MARKER;
                        SectionsRead ++;
                        HaveAll = 1;
                    }*/
                    return 1;
     
                case M_EOI:   // in case it's a tables-only JPEG stream
                    printf("No image in jpeg! ");
                    return 0;
     
                case M_COM// Comment section
                    if (HaveCom){
                        // Discard this section.
                        free(Sections[--SectionsRead].Data);
                        Sections[SectionsRead].Data=0;
                    }else{
                        process_COM(Dataitemlen);
                        HaveCom = 1;
                    }
                    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 (memcmp(Data+2, "Exif", 4) == 0){
                        m_exifinfo->IsExif = process_EXIF((unsigned char *)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(Datamarker);
                    break;
                default:
                    // Skip any other sections.
                    //if (ShowTags) printf("Jpeg section marker 0x%02x size %d ",marker, itemlen);
                    break;
            }
        }
        return 1;
    }
    ////////////////////////////////////////////////////////////////////////////////
    /*--------------------------------------------------------------------------
       Process a EXIF marker
       Describes all the drivel that most digital cameras include...
    --------------------------------------------------------------------------*/
    bool Cexif::process_EXIF(unsigned char * CharBufunsigned int length)
    {
        m_exifinfo->FlashUsed = 0; 
        /* If it's from a digicam, and it used flash, it says so. */
        m_exifinfo->Comments[0] = '';  /* Initial value - null string */
     
        ExifImageWidth = 0;
     
        {   /* Check the EXIF header component */
            static const unsigned char ExifHeader[] = "Exif";
            if (memcmp(CharBuf+0, ExifHeader,6)){
                strcpy(m_szLastError,"Incorrect Exif header");
                return 0;
            }
        }
     
        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 0;
            }
        }
     
        /* Check the next two values for correctness. */
        if (Get16u(CharBuf+8) != 0x2a){
            strcpy(m_szLastError,"Invalid Exif start (1)");
            return 0;
        }
     
        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 0;
        }
     
        unsigned char * LastExifRefd = CharBuf;
     
        /* First directory starts 16 unsigned chars in.  Offsets start at 8 unsigned chars in. */
        if (!ProcessExifDir(CharBuf+14, CharBuf+6, length-6, m_exifinfo, &LastExifRefd))
            return 0;
     
        /* 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 1;
    }
    //--------------------------------------------------------------------------
    // Get 16 bits motorola order (always) for jpeg header stuff.
    //--------------------------------------------------------------------------
    int Cexif::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 order
    --------------------------------------------------------------------------*/
    int Cexif::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 unsigned char order
    --------------------------------------------------------------------------*/
    long Cexif::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 unsigned char order
    --------------------------------------------------------------------------*/
    unsigned long Cexif::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
     
    //2016年11月25日15:55:46 修改TAG_MAKE
    #define TAG_MAKE              0x010F
    #define TAG_COPYRIGHT         0x8298
    #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 Cexif::ProcessExifDir(unsigned char * DirStartunsigned char * OffsetBaseunsigned ExifLength,
                               EXIFINFO * const m_exifinfounsigned char ** const LastExifRefdP )
    {
        int de;
        int a;
        int NumDirEntries;
        unsigned ThumbnailOffset = 0;
        unsigned ThumbnailSize = 0;
     
        NumDirEntries = Get16u(DirStart);
     
        if ((DirStart+2+NumDirEntries*12) > (OffsetBase+ExifLength)){
            strcpy(m_szLastError,"Illegally sized directory");
            return 0;
        }
     
        for (de=0;de<NumDirEntries;de++){
            int TagFormatComponents;
            unsigned char * ValuePtr;
                /* This actually can point to a variety of things; it must be
                   cast to other types when used.  But we use it as a unsigned char-by-unsigned char
                   cursor, so we declare it as a pointer to a generic unsigned char here.
                */
            int BytesCount;
            unsigned char * DirEntry;
            DirEntry = DirStart+2+12*de;
     
            Tag = Get16u(DirEntry);
            Format = Get16u(DirEntry+2);
            Components = Get32u(DirEntry+4);
     
            if ((Format-1) >= NUM_FORMATS) {
                /* (-1) catches illegal zero case as unsigned underflows to positive large */
                strcpy(m_szLastError,"Illegal format code in EXIF dir");
                return 0;
            }
     
            BytesCount = Components * BytesPerFormat[Format];
     
            if (BytesCount > 4){
                unsigned OffsetVal;
                OffsetVal = Get32u(DirEntry+8);
                /* If its bigger than 4 unsigned chars, the dir entry contains an offset.*/
                if (OffsetVal+BytesCount > ExifLength){
                    /* Bogus pointer offset and / or unsigned charcount value */
                    strcpy(m_szLastError,"Illegal pointer offset value in EXIF.");
                    return 0;
                }
                ValuePtr = OffsetBase+OffsetVal;
            }else{
                /* 4 unsigned chars or less and value is in the dir entry itself */
                ValuePtr = DirEntry+8;
            }
     
            if (*LastExifRefdP < ValuePtr+BytesCount){
                /* Keep track of last unsigned char in the exif header that was
                   actually referenced.  That way, we know where the
                   discardable thumbnail data begins.
                */
                *LastExifRefdP = ValuePtr+BytesCount;
            }
     
            /* Extract useful components of tag */
            switch(Tag){
     
                case TAG_MAKE:
                    strncpy(m_exifinfo->CameraMake, (char*)ValuePtr, 31);
                    break;
                    //jsxyhelu添加的代码
                case TAG_COPYRIGHT:
                    strncpy(m_exifinfo->CopyRight, (char*)ValuePtr,MAX_COMMENT-1);
                    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=BytesCount;;){
                        a--;
                        if (((char*)ValuePtr)[a] == ' '){
                            ((char*)ValuePtr)[a] = '';
                        }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 != '' && 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(ValuePtrFormat);
                    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(ValuePtrFormat)*log(2.0)*0.5);
                    }
                    break;
     
                case TAG_BRIGHTNESS:
                    m_exifinfo->Brightness = (float)ConvertAnyFormat(ValuePtrFormat);
                    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(ValuePtrFormat);
                    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(ValuePtrFormat);
                    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(ValuePtrFormat);
                    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(ValuePtrFormat)*log(2.0)));
                    }
                    break;
     
                case TAG_FLASH:
                    if ((int)ConvertAnyFormat(ValuePtrFormat) & 7){
                        m_exifinfo->FlashUsed = 1;
                    }else{
                        m_exifinfo->FlashUsed = 0;
                    }
                    break;
     
                case TAG_ORIENTATION:
                    m_exifinfo->Orientation = (int)ConvertAnyFormat(ValuePtrFormat);
                    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(ValuePtrFormat);
                    if (ExifImageWidth < aExifImageWidth = a;
                    break;
     
                case TAG_FOCALPLANEXRES:
                    m_exifinfo->FocalplaneXRes = (float)ConvertAnyFormat(ValuePtrFormat);
                    break;
     
                case TAG_FOCALPLANEYRES:
                    m_exifinfo->FocalplaneYRes = (float)ConvertAnyFormat(ValuePtrFormat);
                    break;
     
                case TAG_RESOLUTIONUNIT:
                    switch((int)ConvertAnyFormat(ValuePtrFormat)){
                        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(ValuePtrFormat)){
                        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 <schoech(at)gmx(dot)de>
     
                case TAG_EXPOSURE_BIAS:
                    m_exifinfo->ExposureBias = (floatConvertAnyFormat(ValuePtrFormat);
                    break;
     
                case TAG_WHITEBALANCE:
                    m_exifinfo->Whitebalance = (int)ConvertAnyFormat(ValuePtrFormat);
                    break;
     
                case TAG_METERING_MODE:
                    m_exifinfo->MeteringMode = (int)ConvertAnyFormat(ValuePtrFormat);
                    break;
     
                case TAG_EXPOSURE_PROGRAM:
                    m_exifinfo->ExposureProgram = (int)ConvertAnyFormat(ValuePtrFormat);
                    break;
     
                case TAG_ISO_EQUIVALENT:
                    m_exifinfo->ISOequivalent = (int)ConvertAnyFormat(ValuePtrFormat);
                    if ( m_exifinfo->ISOequivalent < 50 ) m_exifinfo->ISOequivalent *= 200;
                    break;
     
                case TAG_COMPRESSION_LEVEL:
                    m_exifinfo->CompressionLevel = (int)ConvertAnyFormat(ValuePtrFormat);
                    break;
     
                case TAG_XRESOLUTION:
                    m_exifinfo->Xresolution = (float)ConvertAnyFormat(ValuePtrFormat);
                    break;
                case TAG_YRESOLUTION:
                    m_exifinfo->Yresolution = (float)ConvertAnyFormat(ValuePtrFormat);
                    break;
     
                case TAG_THUMBNAIL_OFFSET:
                    ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtrFormat);
                    break;
     
                case TAG_THUMBNAIL_LENGTH:
                    ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtrFormat);
                    break;
     
            }
     
            if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET){
                unsigned char * SubdirStart;
                SubdirStart = OffsetBase + Get32u(ValuePtr);
                if (SubdirStart < OffsetBase || 
                    SubdirStart > OffsetBase+ExifLength){
                    strcpy(m_szLastError,"Illegal subdirectory link");
                    return 0;
                }
                ProcessExifDir(SubdirStartOffsetBaseExifLengthm_exifinfoLastExifRefdP);
                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 0;
                }
                ProcessExifDir(SubdirStartOffsetBaseExifLengthm_exifinfoLastExifRefdP);
            }
        }
     
     
        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 1;
    }
    ////////////////////////////////////////////////////////////////////////////////
    /*--------------------------------------------------------------------------
       Evaluate number, be it int, rational, or float from directory.
    --------------------------------------------------------------------------*/
    double Cexif::ConvertAnyFormat(void * ValuePtrint 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 Cexif::process_COM (const unsigned char * Dataint length)
    {
        int ch;
        char Comment[MAX_COMMENT+1];
        int nch;
        int a;
     
        nch = 0;
     
        if (length > MAX_COMMENTlength = MAX_COMMENT// Truncate if it won't fit in our structure.
     
        for (a=2;a<length;a++){
            ch = Data[a];
     
            if (ch == ' ' && Data[a+1] == ' 'continue// Remove cr followed by lf.
     
            if ((ch>=0x20) || ch == ' ' || ch == ' '){
                Comment[nch++] = (char)ch;
            }else{
                Comment[nch++] = '?';
            }
        }
     
        Comment[nch] = ''// Null terminate
     
        //if (ShowTags) printf("COM marker comment: %s ",Comment);
     
        strcpy(m_exifinfo->Comments,Comment);
    }
    ////////////////////////////////////////////////////////////////////////////////
    void Cexif::process_SOFn (const unsigned char * Dataint marker)
    {
        int data_precisionnum_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 ",
        //               ImageInfo.Width, ImageInfo.Height, num_components, data_precision);
    }
    ////////////////////////////////////////////////////////////////////////////////
     
    //#include "stdafx.h"
    //exif 写入函数
    int Cexif::GetEncoderClsid(const WCHARformatCLSIDpClsid)
    {
        UINT num= 0;
        UINT size= 0;
        ImageCodecInfopImageCodecInfoNULL;
        GetImageEncodersSize(&num, &size);
        if(size== 0)
        {
            return -1;
        }
        pImageCodecInfo= (ImageCodecInfo*)(malloc(size));
        if(pImageCodecInfo== NULL)
        {
            return -1;
        }
        GetImageEncoders(numsizepImageCodecInfo);
        for(UINT j=0; jnum; ++j)
        {
            if(wcscmp(pImageCodecInfo[j].MimeTypeformat)== 0)
            {
                *pClsidpImageCodecInfo[j].Clsid;
                free(pImageCodecInfo);
                return j;
            }
        }
        free(pImageCodecInfo);
        return -1;
    }
     
    // 从内存加载图片,失败返回NULL
    BitmapCexif::LoadBitmapFromMemory(const voidmemoryDWORD size)
    {
        Bitmapbmp = NULL;
        IStreamstream = NULL;
        if (CreateStreamOnHGlobal(NULLTRUE, &stream) == S_OK)
        {
            ULARGE_INTEGER uli;
            uli.QuadPart = size;
            stream->SetSize(uli);
            if (stream->Write(memorysizeNULL) == S_OK)
                bmp = new Bitmap(stream);
            stream->Release();
        }
        return bmp;
    }
     
    // 从文件加载图片,不独占文件,失败返回NULL
    Gdiplus::BitmapCexif::LoadBitmapFromFile(const TCHARfile_name)
    {
        Bitmapbmp = NULL;
        HANDLE file_handle = CreateFile(file_nameGENERIC_READFILE_SHARE_READNULLOPEN_EXISTINGFILE_ATTRIBUTE_NORMALNULL);
        if (file_handle != INVALID_HANDLE_VALUE)
        {
            DWORD temp = 0;
            DWORD file_size = GetFileSize(file_handle, &temp);
            if (file_size && !temp)  // 不处理大于4G的文件
            {
                // 将图片文件读到内存后,再从内存创建Bitmap
                unsigned charbuffer = new unsigned char[file_size];
                if (ReadFile(file_handlebufferfile_size, &tempNULL))
                    bmp = LoadBitmapFromMemory(buffertemp);
                delete [] buffer;
            }
            CloseHandle(file_handle);
        }
        return bmp;
    }
     
    //写入exif信息到图片中去,目前只支持jpeg格式
    //返回 0为正常 -1为异常
    //各种tchar wchar cstring的转换,技巧较多.
    int Cexif::WriteExif2Image(CString strFileNameint PropertyTagCString pValue)
    {
        GdiplusStartupInput gdiplusStartupInput;
        ULONG_PTR gdiplusToken;
        GdiplusStartup(&gdiplusToken, &gdiplusStartupInputNULL);
        Status stat;
        CLSID  clsid;
        
        charpropertyValue = new char[100];
        strcpy(propertyValue,pValue);
        
        Bitmapbitmap = LoadBitmapFromFile(strFileName);
        PropertyItempropertyItem = new PropertyItem;
        GetEncoderClsid(L"image/jpeg", &clsid);
     
        propertyItem->id = PropertyTag;//给Tag赋值
        propertyItem->length = 255;  
        propertyItem->type = PropertyTagTypeASCII
        propertyItem->value = propertyValue;
        bitmap->SetPropertyItem(propertyItem);
     
        WCHAR wcharbuf[100];
        CStringW strWide = CT2W(strFileName);
        wcscpy(wcharbuf,strWide);
        stat = bitmap->Save(wcharbuf, &clsidNULL);
     
        if(stat != Ok)
            return -1;
        delete propertyItem;
        delete bitmap;
        GdiplusShutdown(gdiplusToken);
        return 0;
    }
     
  • 相关阅读:
    自定义百度地图链接
    Spring中获取request、response对象的方法
    Windows中使用TortoiseGit提交项目到GitLab配置
    IDEA热部署
    SpringBoot+thymeleaf+mybatis+shiro
    lombok——简化Java代码
    SpringBoot部署到tomcat
    深度学习--深入理解Batch Normalization
    MCMC(一)蒙特卡罗法
    Java反射机制详解
  • 原文地址:https://www.cnblogs.com/jsxyhelu/p/6336799.html
Copyright © 2020-2023  润新知