在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
在VC6中基于dll开发插件用于各种图片显示(BMP/TGA/JPG/GIF/PNG/TIF/ICO/WMF/EMF/...)的更多相关文章
- BEGINNING SHAREPOINT® 2013 DEVELOPMENT 第3章节--SharePoint 2013 开发者工具 SharePoint中基于Web开发
BEGINNING SHAREPOINT® 2013 DEVELOPMENT 第3章节--SharePoint 2013 开发者工具 SharePoint中基于Web开发 之前提到过, ...
- [原创] 毕设---在myeclipes中安装Hadoop开发插件
1.安装Hadoop开发插件 hadoop安装包contrib/目录下有个插件hadoop-0.20.2-eclipse-plugin.jar,拷贝到myeclipse根目录下/dropins目录下. ...
- 如何卸除SDL TRADOS中的自开发插件
去年学着用SDL的例子编译了一个名为SimpleText的插件,每次打开TRADOS 2014时都要提示三次加载插件,很是烦人.但我想卸掉时,却无从下手,不知道怎么办.这个问题纠缠了我很久,今晨心性比 ...
- QT开发实战一:图片显示
测试平台 宿主机平台:Ubuntu 12.04.4 LTS 目标机:Easy-ARM IMX283 目标机内核:Linux 2.6.35.3 QT版本:Qt-4.7.3 Tslib版本:tslib-1 ...
- springcloud中使用dubbo开发rpc服务及调用
spring cloud中基于springboot开发的微服务,是基于http的rest接口,也可以开发基于dubbo的rpc接口. 一,创建goodsService模块 1, 在创建的goodsSe ...
- eclipse SE增加Web开发插件
最近接触了些java项目,之前安装了eclipse SE版本.没有Web开发插件,调试不了Web代码.点击“Window”--“Preference” 左边菜单栏是找不到“Server”项来配置服务器 ...
- WinForm下开发插件DevExpress安装及使用
WinForm下开发插件DevExpress安装及使用在Visual Studio中安装DevExpress开发插件插件的使用方法简单的Demo介绍下载链接:https://pan.baidu.com ...
- 手把手教你基于C#开发WinCC语音报警插件「附源代码」
写在前面 众所周知,WinCC本身是可以利用C脚本或者VBS脚本来做语音报警,但是这种方式的本质是调用已存在的音频文件,想要实现实时播报报警信息是不行的,灵活性还不够,本文主要介绍基于C#/.NET开 ...
- 基于AppDomain的"插件式"开发
很多时候,我们都想使用(开发)USB式(热插拔)的应用,例如,开发一个WinForm应用,并且这个WinForm应用能允许开发人员定制扩展插件,又例如,我们可能维护着一个WinService管理系统, ...
随机推荐
- html基础标签-2-textarea文本域
textarea文本域 <!doctype html> <html lang='zh-cn'> <head> <meta charset='utf-8'> ...
- 【转】CoreData以及MagicalRecord (一)
先粗略的了解下CoreData中的一些核心概念 1. CoreData 的核心概念 先上两幅关键的概念图 (1)NSManagedObjectModel 托管对象模型(MOM)是描述应用程序的数据模型 ...
- Maven java项目管理工具
Maven java项目管理工具 1.安装maven 下载最新的maven 下载地址 http://maven.apache.org/download.cgi 传到要安装的目录 例如/opt/下 # ...
- TCP的流量控制
TCP协议作为一个可靠的面向字节流的传输协议,其可靠性和流量控制由滑动窗口协议保证,而拥塞控制则由控制窗口结合一系列的控制算法实现. 要区分TCP的流量控制和拥塞控制: 流量控制是发送方的发送数据的速 ...
- easyui给select控件绑定change事件
一般的做法是使用jQuery来绑定,例如: $("#id").change(function(){ alert("change事件绑定"); }); 当给sel ...
- JavaScript 入門
<html lang="en"> <head> <meta charset="UTF-8"> <meta na ...
- ICT测试原理
在线测试,ICT,In-Circuit Test,是通过对在线元器件的电性能及电气连接进行测试来检查生产制造缺陷及元器件不良的一种标准测试手段.它主要检查在线的单个元器件以及各电路网络的开.短路情况, ...
- cocos2dx CCTextFieldTTF
CCTextFieldTTF是一个提供文本输入的控件. 先上个简单的例子 CCSize size = __winSize; CCTextFieldTTF* textEdit = CCTextField ...
- HDU 3374 String Problem (KMP+最小最大表示)
[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=3374 [题目大意] 给出一个字符串,求出最小和最大表示是从哪一位开始的,并且输出数量. [题解] ...
- 深度优先搜索算法(DFS)以及leetCode的subsets II
深度优先搜索算法(depth first search),是一个典型的图论算法.所遵循的搜索策略是尽可能“深”地去搜索一个图. 算法思想是: 对于新发现的顶点v,如果它有以点v为起点的未探测的边,则沿 ...