zoukankan      html  css  js  c++  java
  • 在VC6中基于dll开发插件用于各种图片显示(BMP/TGA/JPG/GIF/PNG/TIF/ICO/WMF/EMF/...)

    一、图片显示

    图片显示的方法:

    1.  直接写程序

    2.  第3方库

    3.  调用COM组件的IPicture接口

    4.  使用MFC的CPictureHolder类

    5.  使用GDI+的CImage类(VC6无,从VS2003开始有)

    测试过的方法有1、3、5。

    测试过的格式有BMP/TGA/JPG/GIF/PNG/TIF/ICO/WMF/EMF。

    IPicture接口不支持的格式有:PNG和TIF。

    GDI+支持全部格式。

    二、插件的实现(VC6)

    只做了基于DLL的插件实现试验,基于“公共契约”来实现。

    基本方法:

    ① 使插件和主程序实现相同的公共基类。

    ② 插件只通过公共基类方法与主程序联系,公共基类方法一般为虚函数。

    ③ 主程序通过指向公共基类的指针数组保存插件访问地址列表。

    ④ 主程序根据功能需要遍历插件访问地址列表,通过地址访问插件方法。

    下面以图像显示为例说明插件的实现过程:

    1个主程序Host,4个插件(分别实现对BMP、TGA、JPG、PNG图片的解析)

    1.公共基类

    // IImageParser is the interface that all image parsers must implement

    class IImageParser

    {

    public:

         virtual const char * GetID() = 0; // content to be present in host

         // parses the image file and reads it into a HBITMAP

         virtual HBITMAP ParseFile( const char *fname ) = 0;

         // returns true if the file type is supported

         virtual bool SupportsType( const char *type ) const = 0;

    };

    2.插件实现

    以BMP Parser为例:

    ① BMPParser.h内容如下:

    #include <windows.h>

    #include <stdio.h>

    #include "ImageParser.h"

    class CBMPParser : public IImageParser 

    {

    public:

         CBMPParser();

         virtual ~CBMPParser();

    public:

         virtual const char * GetID() { return "BMP";};

         virtual HBITMAP ParseFile( const char *fname );

         virtual bool SupportsType( const char *type ) const;

        

    private:

         HBITMAP CreateBitmap( int width, int height, void **data );

    };

    static CBMPParser g_BMPParser;

    ② BMPParser.cpp的主要内容如下:

    // Creates a bitmap with the specified size

    HBITMAP CBMPParser::CreateBitmap( int width, int height, void **data )

    {

         BITMAPINFO bmi={sizeof(BITMAPINFOHEADER),width,height,1,24,0,0,0,0,0,0};

         return CreateDIBSection(NULL,&bmi,DIB_RGB_COLORS,data,0,0);

    }

    // Parses a BMP file

    HBITMAP CBMPParser::ParseFile( const char *fname )

    {

         FILE *f=fopen(fname,"rb");

         if (!f) return NULL;

        

         BITMAPFILEHEADER bmfh;

         memset(&bmfh,0,sizeof(bmfh));

         fread(&bmfh,sizeof(bmfh),1,f);

         BITMAPINFOHEADER bmih;

         memset(&bmih,0,sizeof(bmih));

         fread(&bmih,sizeof(bmih),1,f);

         int width=bmih.biWidth;

         int height=bmih.biHeight;

        

         if (bmih.biBitCount!=24) {

             // only 24 bit images are supported

             fclose(f);

             return NULL;

         }

        

         // create the HBITMAP

         void *data;

         HBITMAP bitmap=CreateBitmap(width,height,&data);

         if (!bitmap) {

             fclose(f);

             return NULL;

         }

        

         // read the pixels

         int pitch=(width*3+3)&~3;

         fread(data,pitch,height,f);

        

         fclose(f);

         return bitmap;

    }

    // Notifies the host that the plugin supports the BMP type

    bool CBMPParser::SupportsType( const char *type ) const

    {

         return stricmp(type,".bmp")==0;

    }

    // The host calls this function to get access to the image parser

    extern "C" __declspec(dllexport) IImageParser *GetParser( void ) { return &g_BMPParser; }

    ③ 工作机制

    Host通过方法查询该插件是否支持“某种格式”,如果支持,而调用方法对图片数据进行解析。解析的结果是包含图像像素数据的HBITMAP对象。如果该插件不支持该格式,则继续查询下一插件。

    2.主程序Host的实现

    实验采用标准的MFC架构。

    ① 实现插件管理类

    const int MAX_PARSERS=100;

    #include <windows.h>

    #include <stdio.h>

    #include "ImageParser.h"

    class CImageManager 

    {   

    public:

       CImageManager();

        virtual ~CImageManager();

    private:

        // a prototype for the GetParser function

        typedef IImageParser *(*TGetParser)( void );

        

        // a global list with all parsers

        IImageParser *g_Parsers[MAX_PARSERS];

        int g_NumParsers;

        

    public:

        // the currently displayed bitmap

        HBITMAP g_Bitmap;

    public:

        // Adds the parser to the g_Parsers list

        void AddParser( IImageParser *parser );

        // Loads all plugins in the same folder as the EXE

        void LoadPlugins( CMenu * menu );

        

        // Loads an image file

        BOOL LoadImage( const char *fname );

    };

    ② 应用程序类CHostApp中定义图像插件管理类

    CImageManager* m_imgManager;

    ③ 框架类CMainFrame中加载插件并实现图片文件拖放

    OnCreate()方法中:

    ((CHostApp*)AfxGetApp())->m_imgManager->LoadPlugins(GetMenu()); //加载插件

    DragAcceptFiles(TRUE); //允许拖放

    OnDropFiles()响应函数中:

    // get the file name

    char fname[_MAX_PATH];

    DragQueryFile(hDropInfo,0,fname,_MAX_PATH);

    DragFinish(hDropInfo);

    // load the image file

    ((CHostApp*)AfxGetApp())->m_imgManager->LoadImage(fname);

    CRect rect;

    GetClientRect(&rect);

    InvalidateRect(rect, TRUE);

    ④ 文档类CHostDoc中OnOpenDocument()方法打开图像,并通过插件类进行加载

    ((CHostApp*)AfxGetApp())->m_imgManager->LoadImage(lpszPathName);

    ⑤ 视图类CHostView中OnDraw()方法显示图像

    CImageManager* imgManager = ((CHostApp*)AfxGetApp())->m_imgManager;

    if (NULL != imgManager && NULL != imgManager->g_Bitmap)

    {

         HBITMAP oldBitmap;

         BITMAP bmp;

         CDC memDC;

         CRect rect;

         memDC.CreateCompatibleDC(pDC);

         GetClientRect(&rect);

         oldBitmap = (HBITMAP)memDC.SelectObject(imgManager->g_Bitmap);

         GetObject(imgManager->g_Bitmap, sizeof(bmp), &bmp);

         pDC->BitBlt(0, 0, bmp.bmWidth, bmp.bmHeight, &memDC, 0, 0, SRCCOPY);

         memDC.SelectObject(oldBitmap);

         memDC.DeleteDC();

    }

    完毕!

    三、IPicture接口解析图像文件

    参见《利用COM组件IPicture读取jpg、gif、bmp图片文件数据和显示图片的两个函数》:http://www.cnblogs.com/zuollblog/archive/2010/04/21/1716983.html

    显示图片格式的程序中只需要解析到HBITMAP为止,修改后的函数如下:

    HBITMAP CJPGParser::ParseFile( const char *pName )

    {

        HDC            hdcTemp; //DC用来保存位图 

        HBITMAP     hbmpTemp;   // 保存临时位图 

        IPicture    *pPicture;  // 定义IPicture Interface 

        OLECHAR     wszPath[MAX_PATH+1]; // 图片的完全路径 

        char        szPath[MAX_PATH+1];  // 图片的完全路径 

        long        lWidth; // 图像宽度 

        long        lHeight;    // 图像高度 

        long        lWidthPixels;    // 图像的宽带(以像素为单位) 

        long        lHeightPixels;   // 图像的高带(以像素为单位) 

         strcpy(szPath, pName);  // 把路径拷贝到 szPath

        // 把ASCII码转化为Unicode标准码 

        MultiByteToWideChar(CP_ACP, 0, szPath, -1, wszPath, MAX_PATH);

        HRESULT hr = OleLoadPicturePath(wszPath, 0, 0, 0, IID_IPicture, (void**)&pPicture); 

        if(FAILED(hr))  // 如果导入失败 

        { 

            MessageBox (HWND_DESKTOP, "图片导入失败!/n(TextureLoad Failed!)", "Error", MB_OK | MB_ICONEXCLAMATION); 

            return NULL;

        }  

        hdcTemp = CreateCompatibleDC(GetDC(NULL)); // 建立窗口设备描述表  

        if(!hdcTemp)    // 建立失败? 

        { 

            pPicture->Release(); // 释放IPicture 

            MessageBox (HWND_DESKTOP, "图片导入失败!/n(TextureLoad Failed!)", "Error", MB_OK | MB_ICONEXCLAMATION); 

            return NULL;

        } 

        pPicture->get_Width(&lWidth);    // 取得IPicture 宽度 (转换为Pixels格式) 

        lWidthPixels = MulDiv(lWidth, GetDeviceCaps(hdcTemp, LOGPIXELSX), 2540); 

        pPicture->get_Height(&lHeight);  // 取得IPicture 高度 (转换为Pixels格式) 

        lHeightPixels = MulDiv(lHeight, GetDeviceCaps(hdcTemp, LOGPIXELSY), 2540); 

        BITMAPINFO  bi = {0};     // 位图的类型 

         void       *pData = 0; // 指向位图Data的指针 

        bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); // 设置结构大小 

        bi.bmiHeader.biBitCount = 32;                // 32 位 

        bi.bmiHeader.biWidth    = lWidthPixels; // 宽度像素值 

        bi.bmiHeader.biHeight   = lHeightPixels;    // 高度像素值  

        bi.bmiHeader.biCompression  = BI_RGB;            // RGB 格式 

        bi.bmiHeader.biPlanes   = 1;                    // 一个位平面 

        // 建立一个位图这样我们可以指定颜色和深度 并访问每位的值 

        hbmpTemp = CreateDIBSection(hdcTemp, &bi, DIB_RGB_COLORS, &pData, 0, 0); 

        if(!hbmpTemp)   // 建立失败? 

        { 

            DeleteDC(hdcTemp);   // 删除设备描述表 

            pPicture->Release();   // 释放IPicture 

            MessageBox (HWND_DESKTOP, "图片导入失败!/n(TextureLoad Failed!)", "Error", MB_OK | MB_ICONEXCLAMATION); 

            return NULL;

        }    

        SelectObject(hdcTemp, hbmpTemp); //选择临时DC句柄和临时位图对象 

        // 在位图上绘制IPicture 

        pPicture->Render(hdcTemp, 0, 0, lWidthPixels, lHeightPixels, 0, lHeight, lWidth, -lHeight, 0); 

        DeleteDC(hdcTemp);  // 删除设备描述表  

        pPicture->Release(); // 释放 IPicture 

         return hbmpTemp;

    }

    基本原理是建立一个虚拟的HDC,将图绘制到该HDC上即可获得图像像素数据。

    四、使用GDI+解析图像文件

    VC6不支持GDI+,但可以将VS2003以上版的CImage类通过DLL包括后引入VC6。针对上文的图像显示,包装的方法有两种:

    1.只用CImage解析图片文件,返回HBITMAP

    参见《VC6 CImage 加载jpg png bmp》:http://hi.baidu.com/crazyonline/blog/item/3bad6959b2d31b232934f0f4.html

    我的基于VS2008的 DLL的实现代码如下:

    ① Image2008.h中的代码:

    #ifndef AFX_EXT_CLASS

    #define AFX_EXT_CLASS __declspec(dllimport)

    #endif

    extern "C" AFX_EXT_CLASS HBITMAP LoadAtlImage(const char * pszFileName);

    ② Image2008.cpp中的代码:

    #define AFX_EXT_CLASS __declspec(dllexport)

    #include "stdafx.h"

    #include "Image08.h"

    #include <atlimage.h>

    #include <atlconv.h>

    HBITMAP LoadAtlImage(const char * pszFileName)

    {

         CImage img;

         USES_CONVERSION;

         HRESULT hr = img.Load(A2T(pszFileName));

         if (SUCCEEDED(hr))

             return img.Detach();

         else

             return NULL;

    }

    ③ 图片文件解析过程

    typedef HBITMAP (*LoadImageFunc)(const char *);

    LoadImageFunc funcLoadImage = NULL;

    HBITMAP hBitmap = NULL;

    char szPath[_MAX_PATH];

    GetModuleFileName(NULL,szPath,_MAX_PATH);

    *PathFindFileName(szPath) = 0;

    strcat(szPath, "//CImage08.dll");

    HMODULE hModule=LoadLibrary(szPath);

    if(hModule)

    {

         funcLoadImage = (LoadImageFunc)GetProcAddress(hModule, "LoadAtlImage");

         if (funcLoadImage)

             hBitmap = funcLoadImage(pName);

         FreeLibrary(hModule);

    }

    return hBitmap;

    2.逐函数包括CImage

    参见《VC6如何使用VS2005中的CImage类功能》:http://blog.csdn.net/wangji163163/archive/2007/09/11/1780508.aspx

    我的基于VS2008的 DLL的实现代码如下:

    ① Image2008.h中的代码:

    #ifndef AFX_EXT_CLASS

    #define AFX_EXT_CLASS __declspec(dllimport)

    #endif

     

    class AFX_EXT_CLASS CImage08

    {

    private:

         void * m_pImage;   //内部数据

    public:

         CImage08(void);

         virtual ~CImage08(void);

    public:

         inline HRESULT Load(LPCSTR pszFileName);  //装载图片

         inline BOOL IsNull();  //资源是否存在

         inline BOOL Draw(HDC hDestDC, const RECT& rectDest);    //指定设备和区域画图

         inline operator HBITMAP();  //转换为GDI中的HBITMAP, 从而供GDI中的CDC等类使用

    };

    ② Image2008.cpp中的代码:

    #define AFX_EXT_CLASS __declspec(dllexport)

    #include "stdafx.h"

    #include "Image08.h"

    #include <atlimage.h>

    #include <atlconv.h>

    CImage08::CImage08(void)

    {

         m_pImage=NULL;

         m_pImage=new CImage;

    }

    CImage08::~CImage08(void)

    {

         if(m_pImage)

         {

             delete m_pImage;

             m_pImage=NULL;

         }

    }

    inline HRESULT CImage08::Load( LPCSTR pszFileName )

    {

         USES_CONVERSION;

         return ((CImage*)m_pImage)->Load(A2T(pszFileName));

    }

    inline BOOL CImage08::IsNull()

    {

         return ((CImage*)m_pImage)->IsNull();

    }

    inline BOOL CImage08::Draw( HDC hDestDC, const RECT& rectDest )

    {

         return ((CImage*)m_pImage)->Draw(hDestDC,rectDest );

    }

    inline  CImage08::operator HBITMAP()

    {

         return ((CImage*)m_pImage)->operator HBITMAP();

    }

    使用方法与普通的DLL无异,将编译后的.dll文件、.lib文件、.h文件引入Host主程序即可

    ③ 图片文件解析过程

    CImage08 img;

    HRESULT rtl = img.Load(pName);

    if (rtl == S_OK)

         return img;

    else

         return NULL; 

    五、结束语

    使用的GDI+之后,确实感到VC6真的过时了。GDI+几乎可以打开常见的所有图片文件。VC6几乎打开所有的图片文件都需要编程,费时费力,效果还不一定会好。如果不是万不得已,或者十分怀旧,或者十分偏爱VC6的话,能换还是换个更高点版本的VC吧!

    参考文献:

    1.VC++实现插件编程,http://blog.csdn.net/wyyw21/archive/2006/08/31/1151839.aspx

    2.Plugin System – an alternative to GetProcAddress and interfaces, http://www.codeproject.com/KB/DLL/PluginSystem.aspx

    3.利用COM组件IPicture读取jpg、gif、bmp图片文件数据和显示图片的两个函数,http://www.cnblogs.com/zuollblog/archive/2010/04/21/1716983.html

    4.VC6 CImage 加载jpg png bmp,http://hi.baidu.com/crazyonline/blog/item/3bad6959b2d31b232934f0f4.html

    5.VC6如何使用VS2005中的CImage类功能,http://blog.csdn.net/wangji163163/archive/2007/09/11/1780508.aspx

    http://blog.csdn.net/yuvmen/article/details/5879371

  • 相关阅读:
    javascript静态页面传值的三种方法分享
    分布式缓存HttpRuntime.cache应用到单点登陆中_优化登陆
    C# 委托的三种调用示例(同步调用 异步调用 异步回调)
    使用SQL Server视图的优缺点
    浅析深究什么是SOA?
    IIS启用GZip压缩
    BI
    ASP.NET运行原理
    力扣算法:N 皇后
    力扣算法:基本计算器Ⅱ
  • 原文地址:https://www.cnblogs.com/findumars/p/5086804.html
Copyright © 2011-2022 走看看