一、图片显示

图片显示的方法:

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

在VC6中基于dll开发插件用于各种图片显示(BMP/TGA/JPG/GIF/PNG/TIF/ICO/WMF/EMF/...)的更多相关文章

  1. BEGINNING SHAREPOINT&#174; 2013 DEVELOPMENT 第3章节--SharePoint 2013 开发者工具 SharePoint中基于Web开发

    BEGINNING SHAREPOINT® 2013 DEVELOPMENT 第3章节--SharePoint 2013 开发者工具 SharePoint中基于Web开发         之前提到过, ...

  2. [原创] 毕设---在myeclipes中安装Hadoop开发插件

    1.安装Hadoop开发插件 hadoop安装包contrib/目录下有个插件hadoop-0.20.2-eclipse-plugin.jar,拷贝到myeclipse根目录下/dropins目录下. ...

  3. 如何卸除SDL TRADOS中的自开发插件

    去年学着用SDL的例子编译了一个名为SimpleText的插件,每次打开TRADOS 2014时都要提示三次加载插件,很是烦人.但我想卸掉时,却无从下手,不知道怎么办.这个问题纠缠了我很久,今晨心性比 ...

  4. QT开发实战一:图片显示

    测试平台 宿主机平台:Ubuntu 12.04.4 LTS 目标机:Easy-ARM IMX283 目标机内核:Linux 2.6.35.3 QT版本:Qt-4.7.3 Tslib版本:tslib-1 ...

  5. springcloud中使用dubbo开发rpc服务及调用

    spring cloud中基于springboot开发的微服务,是基于http的rest接口,也可以开发基于dubbo的rpc接口. 一,创建goodsService模块 1, 在创建的goodsSe ...

  6. eclipse SE增加Web开发插件

    最近接触了些java项目,之前安装了eclipse SE版本.没有Web开发插件,调试不了Web代码.点击“Window”--“Preference” 左边菜单栏是找不到“Server”项来配置服务器 ...

  7. WinForm下开发插件DevExpress安装及使用

    WinForm下开发插件DevExpress安装及使用在Visual Studio中安装DevExpress开发插件插件的使用方法简单的Demo介绍下载链接:https://pan.baidu.com ...

  8. 手把手教你基于C#开发WinCC语音报警插件「附源代码」

    写在前面 众所周知,WinCC本身是可以利用C脚本或者VBS脚本来做语音报警,但是这种方式的本质是调用已存在的音频文件,想要实现实时播报报警信息是不行的,灵活性还不够,本文主要介绍基于C#/.NET开 ...

  9. 基于AppDomain的"插件式"开发

    很多时候,我们都想使用(开发)USB式(热插拔)的应用,例如,开发一个WinForm应用,并且这个WinForm应用能允许开发人员定制扩展插件,又例如,我们可能维护着一个WinService管理系统, ...

随机推荐

  1. C# 创建文件时,文件夹不存在,如何自动创建文件夹

    c# 创建文件时怎么创建文件夹?strhtml=......StreamWriter sw=new StreamWriter("D:/test/1.aspx",false);sw. ...

  2. CocoaPods 出现 OTHER_LDFLAGS 错误的解决方法

    CocoaPods 出现 OTHER_LDFLAGS 错误的解决方法 在一些项目中运行 pod install 后经常会出现如下错误 [!] The target `项目名 [Debug]` over ...

  3. hibernate sql查询后对象转换成实体类

    在多表查询的时候使用hibernate的sql查询的时候,一般返回的是object[]数组,或者可以使用  session.createSQLQuery(sql).setResultTransform ...

  4. android netty5.0 编译时 java.lang.NoClassDefFoundError: io.netty.channel.nio.NioEventLoopGroup

    android netty5.0 编译时 java.lang.NoClassDefFoundError: io.netty.channel.nio.NioEventLoopGroup 复制netty包 ...

  5. java附件上传下载磁盘版

    ACTION public class UploadAction extends BaseAction { private static final long serialVersionUID = 1 ...

  6. PADS LAYOUT到底怎么走线

    PADS LAYOUT走线,是不是转角要自己手动慢慢转角啊?不能像PROTEL中那样自动转角吗 自己手动转角老是转不好,出现许多线头,对不齐,是不是我操作有误啊 走线的过程中,可以试试这个,切换端点. ...

  7. 我的 Azure VM 为何会重新启动?

    在客户创建的客服案件中, Azure VM意外重启是一个常见的问题,客户要求客服确定重新启动的原因.希望下面的详细说明能够帮助您了解 Azure VM重新启动的原因. WindowsAzure大约 ...

  8. Min Stack (LeetCode) tweak it to avoid Memory Limit Exceeded

    class MinStack { public: void push(int x) { if(values.empty()) { values.push_back(x); min_indices.pu ...

  9. wndows make images

    配置文件/etc/xen/mywindows.内容如下 import os, re arch_libdir = 'lib' arch = os.uname()[4] if os.uname()[0] ...

  10. CNZZ公告:近期无法获取百度关键词

    今天登录cnzz网站统计,出现一条公告,说是“关于近期无法获取百度关键词的公告”,内容如下: 近日部分用户反馈百度搜索词流量出现不同程度的下降.经排查,是由于百度搜索引擎调整了URL规则,取消了来源U ...