www.pudn.com > isee_code01231.zip > EmfModule.cpp
/********************************************************************
EmfModule.cpp - ISee图像浏览器—EMF图像读写模块实现文件
版权所有(C) 2000 VCHelp-coPathway-ISee workgroup member - YZ
这一程序是自由软件,你可以遵照自由软件基金会出版的GNU 通用公共许
可证条款来修改和重新发布这一程序。或者用许可证的第二版,或者(根
据你的选择)用任何更新的版本。
发布这一程序的目的是希望它有用,但没有任何担保。甚至没有适合特定
目地的隐含的担保。更详细的情况请参阅GNU通用公共许可证。
你应该已经和程序一起收到一份GNU通用公共许可证的副本。如果还没有,
写信给:
The Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
MA02139, USA
如果你在使用本软件时有什么问题或建议,用以下地址可以与我们取得联
系:
http://isee.126.com
http://www.vchelp.net
或:
iseesoft@china.com
编写人:YZ
E-Mail:yzfree@sina.com 或 yzfree@yeah.net
文件版本:
Beta 1
Build 00625
Date 2000-6-25
********************************************************************/
#include "stdafx.h"
#include "EmfModule.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
static char ModuleProcessImgType[]="EMF"; // 本模块能处理的图像类型
static char WriterList[]="YZ"; // 本模块的作者列表
static char WriterMess[]="Hi,EMF模块工作的正常吗^_^"; // 作者留言
//
// Note!
//
// If this DLL is dynamically linked against the MFC
// DLLs, any functions exported from this DLL which
// call into MFC must have the AFX_MANAGE_STATE macro
// added at the very beginning of the function.
//
// For example:
//
// extern "C" BOOL PASCAL EXPORT ExportedFunction()
// {
// AFX_MANAGE_STATE(AfxGetStaticModuleState());
// // normal function body here
// }
//
// It is very important that this macro appear in each
// function, prior to any calls into MFC. This means that
// it must appear as the first statement within the
// function, even before any object variable declarations
// as their constructors may generate calls into the MFC
// DLL.
//
// Please see MFC Technical Notes 33 and 58 for additional
// details.
//
// 在图像读写模块中,如果想分配内存,请使用API函数GlobalAlloc()
// ,如果想释放内存请使用GlobalFree()函数。不要使用诸如:new
// 、malloc()等函数。这是为了使各模块之间可以异地释放内存。
//
//
/////////////////////////////////////////////////////////////////////////////
// CEmfModuleApp
BEGIN_MESSAGE_MAP(CEmfModuleApp, CWinApp)
//{{AFX_MSG_MAP(CEmfModuleApp)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CEmfModuleApp construction
CEmfModuleApp::CEmfModuleApp()
{
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}
/////////////////////////////////////////////////////////////////////////////
// The one and only CEmfModuleApp object
CEmfModuleApp theApp;
// 接口函数声明 — 第一层,唯一与外界联系的接口
int WINAPI AccessEMFModule(INFOSTR *pInfo)
{
// 这个函数可以不作修改的使用,除非你的返回值多于两种。
switch(pInfo->comm)
{
case CMD_GETPROCTYPE: // 获取本模块能处理的图像类型
_fnCMD_GETPROCTYPE(pInfo);
break;
case CMD_GETWRITERS: // 获取本模块的作者列表,多人时用逗号分隔
_fnCMD_GETWRITERS(pInfo);
break;
case CMD_GETWRITERMESS: // 获取作者们的留言
_fnCMD_GETWRITERMESS(pInfo);
break;
case CMD_GETBUILDID: // 获取图像模块内部版本号
_fnCMD_GETBUILDID(pInfo);
break;
case CMD_IS_VALID_FILE: // 判断指定文件是否是有效的WMF文件
_fnCMD_IS_VALID_FILE(pInfo);
break;
case CMD_GET_FILE_INFO: // 获取指定文件的信息
_fnCMD_GET_FILE_INFO(pInfo);
break;
case CMD_LOAD_FROM_FILE: // 从指定图像文件中读取数据
_fnCMD_LOAD_FROM_FILE(pInfo);
break;
case CMD_SAVE_TO_FILE: // 将数据保存到指定文件中
_fnCMD_SAVE_TO_FILE(pInfo);
break;
case CMD_IS_SUPPORT: // 查询某个命令是否被支持
_fnCMD_IS_SUPPORT(pInfo);
break;
case CMD_RESIZE: // 从新获取指定尺寸的图像位数据(只适用于矢量图像)
_fnCMD_RESIZE(pInfo);
break;
default:
pInfo->result = ER_ILLCOMM; // 非法命令
ASSERT(FALSE); // 调用者的程序设计有问题 :-)
break;
}
// 执行命令成功返回1, 失败返回0
return (pInfo->result==ER_SUCCESS)? 1:0;
}
// 命令解释函数 — 第二层解释函数
//********************************************************************//
// 操作命令解释函数---解释:CMD_IS_SUPPORT命令
// 查询某个命令是否被支持
void _fnCMD_IS_SUPPORT(INFOSTR *pInfo)
{
ASSERT(pInfo->result == ER_EMPTY);
switch(pInfo->annexdata.cmAnnData)
{
case CMD_GETPROCTYPE: // 获取本模块能处理的图像类型
pInfo->result = ER_SUCCESS;
break;
case CMD_GETWRITERS: // 获取本模块的作者列表,多人时用逗号分隔
pInfo->result = ER_SUCCESS;
break;
case CMD_GETWRITERMESS: // 获取作者们的留言
pInfo->result = ER_SUCCESS;
break;
case CMD_GETBUILDID: // 获取图像模块内部版本号
pInfo->result = ER_SUCCESS;
break;
case CMD_IS_VALID_FILE: // 判断指定文件是否是有效的EMF文件
pInfo->result = ER_SUCCESS;
break;
case CMD_GET_FILE_INFO: // 获取指定文件的信息
pInfo->result = ER_SUCCESS;
break;
case CMD_LOAD_FROM_FILE: // 从指定图像文件中读取数据
pInfo->result = ER_SUCCESS;
break;
case CMD_SAVE_TO_FILE: // 将数据保存到指定文件中
pInfo->result = ER_NOTSUPPORT;
break;
case CMD_IS_SUPPORT: // 查询某个命令是否被支持
pInfo->result = ER_SUCCESS;
break;
case CMD_RESIZE: // 获取指定尺寸的图像(只适用于矢量图像)
pInfo->result = ER_SUCCESS;
break;
default:
pInfo->result = ER_NOTSUPPORT;
break;
}
}
// 操作命令解释函数---解释:CMD_GETPROCTYPE命令
// 获取本模块能处理的图像类型,如:BMP,PCX等等
void _fnCMD_GETPROCTYPE(INFOSTR *pInfo)
{
// 这是预定义的函数代码,你可以不必修改的使用。
// 根据接口定义,此时附加数据应被清空为0,所以下此断言
ASSERT(pInfo->annexdata.scAnnData[0] == 0);
ASSERT(pInfo->result == ER_EMPTY);
// 复制能处理的类型字符串
::CopyMemory((PVOID)pInfo->annexdata.scAnnData, (PVOID)ModuleProcessImgType,
sizeof(ModuleProcessImgType));
pInfo->result = ER_SUCCESS;
}
// 操作命令解释函数---解释:CMD_GETWRITER命令
// 获取本模块的作者列表,多人时用逗号分隔
void _fnCMD_GETWRITERS(INFOSTR *pInfo)
{
// 这是预定义的函数代码,你可以不必修改的使用。
// 根据接口定义,此时附加数据应被清空为0,所以下此断言
ASSERT(pInfo->annexdata.scAnnData[0] == 0);
ASSERT(pInfo->result == ER_EMPTY);
// 复制开发者名单串
::CopyMemory((PVOID)pInfo->annexdata.scAnnData, (PVOID)WriterList,
sizeof(WriterList));
pInfo->result = ER_SUCCESS;
}
// 操作命令解释函数---解释:CMD_GETWRITERMESS命令
// 获取作者们的留言
void _fnCMD_GETWRITERMESS(INFOSTR *pInfo)
{
// 这是预定义的函数代码,你可以不必修改的使用。
// 根据接口定义,此时附加数据应被清空为0,所以下此断言
ASSERT(pInfo->annexdata.scAnnData[0] == 0);
ASSERT(pInfo->result == ER_EMPTY);
// 复制开发者们的留言字符串
::CopyMemory((PVOID)pInfo->annexdata.scAnnData, (PVOID)WriterMess,
sizeof(WriterMess));
pInfo->result = ER_SUCCESS;
}
// 操作命令解释函数---解释:CMD_GETBUILDID命令
// 获取图像模块内部版本号
void _fnCMD_GETBUILDID(INFOSTR *pInfo)
{
// 这是预定义的函数代码,你可以不必修改的使用。
// 根据接口定义,此时annexdata.dwAnnData应被设为0,所以下此断言
ASSERT(pInfo->annexdata.dwAnnData == 0);
ASSERT(pInfo->result == ER_EMPTY);
// 填写内部版本号码
pInfo->annexdata.dwAnnData = MODULE_BUILDID;
pInfo->result = ER_SUCCESS;
}
// 操作命令解释函数---解释:CMD_IS_VALID_FILE命令
// 判断指定文件是否是有效的WMF文件
void _fnCMD_IS_VALID_FILE(INFOSTR *pInfo)
{
CFile file;
DWORD dwFileSize;
ENHMETAHEADER emfHeader;
// 检验入口参数是否符合接口定义
ASSERT(pInfo->result == ER_EMPTY);
ASSERT(pInfo->annexdata.iAnnData == 0);
ASSERT(::strlen(pInfo->filename));
ASSERT(pInfo->state == PKST_NOTVER);
// 先判断指定的文件是否存在
if (!IsFileExist(pInfo->filename))
pInfo->result = ER_COMMINFOERR;
else
{
// 打开指定文件
if (!file.Open(pInfo->filename, CFile::modeRead))
{
pInfo->result = ER_FILERWERR; // 打开文件时出错
return;
}
dwFileSize = file.GetLength();
// 用长度判断
if (dwFileSize < sizeof(ENHMETAHEADER))
{
pInfo->result = ER_SUCCESS; // 这不是一个EMF文件
pInfo->annexdata.iAnnData = 0; // EMF文件的长度起码大于
file.Close(); // ENHMETAHEADER结构的长度
return;
}
file.Seek(0, CFile::begin);
// 读取文件头
if (file.Read(&emfHeader, sizeof(ENHMETAHEADER)) != sizeof(ENHMETAHEADER))
{
pInfo->result = ER_FILERWERR; // 读文件时出错
file.Close();
return;
}
// 检查头部标志
if ((emfHeader.iType != EMR_HEADER)||
(emfHeader.dSignature != ENHMETA_SIGNATURE))
//(emfHeader.nSize != sizeof(ENHMETAHEADER))|| // 更严厉的检查
//(emfHeader.nVersion < 0x10000)||
//(emfHeader.sReserved != 0)
{
pInfo->result = ER_SUCCESS;
pInfo->annexdata.iAnnData = 0; // 这可能是一个受损的EMF文件
file.Close();
return;
}
// 到此,表明该文件是一个有效的EMF文件,iAnnData变量设为1
pInfo->annexdata.iAnnData = 1;
pInfo->state = PKST_PASSVER;
pInfo->result = ER_SUCCESS;
file.Close();
}
}
// 操作命令解释函数---解释:CMD_GET_FILE_INFO命令
// 获取指定文件的信息
void _fnCMD_GET_FILE_INFO(INFOSTR *pInfo)
{
CFile file;
ENHMETAHEADER emfHeader;
// 检验入口参数是否符合接口定义
ASSERT(pInfo->result == ER_EMPTY);
ASSERT(::strlen(pInfo->filename));
// 此时,该文件必需是一个已存在的、并且是有效的EMF文件
ASSERT(pInfo->state == PKST_PASSVER);
// 客户模块必需要先将imginfo清空为0
ASSERT(pInfo->imginfo.imgtype == IMT_NULL);
// 打开指定文件
if (!file.Open(pInfo->filename, CFile::modeRead))
{
pInfo->result = ER_FILERWERR;
return;
}
file.Seek(0, CFile::begin);
// 读取EMF文件头
if (file.Read(&emfHeader, sizeof(ENHMETAHEADER)) != sizeof(ENHMETAHEADER))
{
pInfo->result = ER_FILERWERR; // 读文件时出错
file.Close();
return;
}
HWND hWnd = ::GetDesktopWindow();
HDC dc = ::GetDC(hWnd);
// 先取得屏幕以毫米为单位的宽度和高度,然后乘以100就变为以0.01毫米
// 为单位的宽度及高度。然后除以屏幕的以像素为单位的宽度和高度就计算
// 出当前每个像素所占的0.01毫米的单位个数。最后再以图像的以0.01毫米
// 为单位的宽度和高度除以前面计算出的值就可计算出图像在当前显示设备
// 上的以像素为单位的尺寸了。
float fpw = (((float)::GetDeviceCaps(dc, HORZSIZE))*100)/((float)::GetDeviceCaps(dc, HORZRES));
float fph = (((float)::GetDeviceCaps(dc, VERTSIZE))*100)/((float)::GetDeviceCaps(dc, VERTRES));
float fx = ((float)(emfHeader.rclFrame.right-emfHeader.rclFrame.left))/fpw + (float)0.5;
float fy = ((float)(emfHeader.rclFrame.bottom-emfHeader.rclFrame.top))/fph + (float)0.5;
LPIMAGEINFOSTR lpImgInfoStr = &pInfo->imginfo;
// 获取文件的长度、图像的宽度、高度等信息
lpImgInfoStr->imgtype = IMT_VECTORSTATIC;
lpImgInfoStr->imgformat = IMF_EMF;
lpImgInfoStr->filesize = file.GetLength();
lpImgInfoStr->width = (int)fx;
lpImgInfoStr->height = (int)fy;
::ReleaseDC(hWnd, dc);
// 这是个虚值,没有意义
lpImgInfoStr->bitcount = 24;
lpImgInfoStr->compression = ICS_GDIRECORD;
// 以下这个0值没有意义,该参数只对BMP图像文件有意义
lpImgInfoStr->linesize = 0;
// 静态EMF文件只有一个图像,所以设为1
lpImgInfoStr->imgnumbers = 1;
// 可以被编辑,因为它是静态图像。
lpImgInfoStr->imgchang = 0;
// 获取文件最后的修改日期(月在高字节,日在低字节)
CFileStatus status;
file.GetStatus(status);
lpImgInfoStr->year = (WORD)status.m_mtime.GetYear();
lpImgInfoStr->monday = (WORD)status.m_mtime.GetMonth();
lpImgInfoStr->monday <<= 8;
lpImgInfoStr->monday |= (WORD)status.m_mtime.GetDay();
// 获取文件最后的修改时间(字序:最高字节—0, 2—时,1—分,最低字节—秒)
lpImgInfoStr->time = status.m_mtime.GetHour();
lpImgInfoStr->time <<= 8;
lpImgInfoStr->time |= status.m_mtime.GetMinute();
lpImgInfoStr->time <<= 8;
lpImgInfoStr->time |= status.m_mtime.GetSecond();
lpImgInfoStr->time &= 0xffffff;
// 关闭文件
file.Close();
// 设置出口数据
pInfo->state = PKST_PASSINFO;
pInfo->result = ER_SUCCESS;
}
// 操作命令解释函数---解释:CMD_LOAD_FROM_FILE命令
// 从指定图像文件中读取数据
void _fnCMD_LOAD_FROM_FILE(INFOSTR *pInfo)
{
// 检验入口参数是否符合接口定义
ASSERT(pInfo->result == ER_EMPTY);
ASSERT(::strlen(pInfo->filename));
// 此时,该文件必需是一个已存在的、有效的EMF文件,并且数据包中
// 含有该文件的信息(imginfo结构中)
ASSERT(pInfo->state == PKST_PASSINFO);
ASSERT(pInfo->imginfo.imgformat == IMF_EMF);
ASSERT(pInfo->pImgInfo == NULL);
// 必需设置标准图像格式信息
ASSERT(pInfo->sDIBInfo.bmi.biSize == sizeof(BITMAPINFOHEADER));
ASSERT(pInfo->pLineAddr != NULL);
ASSERT(pInfo->_pbdata != NULL);
HENHMETAFILE hEnhMeta;
BYTE *pby;
HBITMAP bmp, oldbmp;
HWND hWnd = ::GetDesktopWindow();
HDC hDC = ::GetDC(hWnd);
HDC hComDC;
int sizeline;
ASSERT(hWnd);
if (pInfo->fpProgress)
{
if ((*pInfo->fpProgress)(RWPROGRESSSIZE, 10))
{ // 如果进度函数返回1,则说明用户想中断操作,返回。
::ReleaseDC(hWnd, hDC);
pInfo->result = ER_USERBREAK;
return;
}
}
if ((hEnhMeta = ::GetEnhMetaFile((LPCTSTR)pInfo->filename)) != NULL)
{
ASSERT(pInfo->sDIBInfo.bmi.biPlanes == 1);
// 可接收三中格式的图像位数据请求:16,24,32。并且要求数据包位
// 数据缓冲区是DIB格式(扫描行以4字节对齐)
ASSERT((pInfo->sDIBInfo.bmi.biBitCount==16)||(pInfo->sDIBInfo.bmi.biBitCount==24)||(pInfo->sDIBInfo.bmi.biBitCount==32));
// 创建用于存放图像位数据的临时缓冲区
bmp = ::CreateDIBSection(hDC, (CONST BITMAPINFO*)&pInfo->sDIBInfo,
DIB_RGB_COLORS, (VOID**)&pby, NULL, 0);
hComDC = ::CreateCompatibleDC(hDC);
if ((bmp == NULL)||(hComDC == NULL)) // 错误检查
{
::DeleteEnhMetaFile(hEnhMeta);
::ReleaseDC(hWnd, hDC);
if (bmp) ::DeleteObject(bmp);
if (hComDC) ::DeleteDC(hComDC);
pInfo->result = ER_SYSERR;
return;
}
ASSERT(pby); // 此时pby中必然存在bmp位数据缓冲区的地址
oldbmp = (HBITMAP)::SelectObject(hComDC, bmp);
// 设置背景色为缺省窗口色
::FillRect(hComDC, &CRect(0,0,pInfo->sDIBInfo.bmi.biWidth, pInfo->sDIBInfo.bmi.biHeight), (HBRUSH)(COLOR_WINDOW+1));
// 绘制EMF图像
::PlayEnhMetaFile(hComDC, hEnhMeta, CRect(0,0,pInfo->sDIBInfo.bmi.biWidth, pInfo->sDIBInfo.bmi.biHeight));
::SelectObject(hComDC, oldbmp);
::DeleteDC(hComDC);
int height = pInfo->sDIBInfo.bmi.biHeight;
ASSERT(height != 0);
// DIB缓冲区中每一扫描行都是4字节对齐的,所以使用了DIBSCANLINE_WIDTHBYTES宏
sizeline = DIBSCANLINE_WIDTHBYTES(pInfo->sDIBInfo.bmi.biWidth*pInfo->sDIBInfo.bmi.biBitCount);
// 复制图像到数据包中
PBYTE lpsou = pby;
ASSERT(pInfo->pLineAddr[0] != NULL); // 如果数据包中的行地址没有填写,就会引发此断言
if (height < 0) // 正向DIB缓冲区(top-down)
{
for (int i=0;ipLineAddr[i], (CONST VOID *)lpsou, sizeline);
lpsou += sizeline;
}
}
else // 倒向
{
for (int i=0;ipLineAddr[height-i-1], (CONST VOID *)lpsou, sizeline);
lpsou += sizeline;
}
}
::DeleteObject(bmp);
::DeleteEnhMetaFile(hEnhMeta);
// 成功
pInfo->pImgInfo = NULL; // EMF模块不返回原始图像数据
pInfo->state = PKST_INFOANDBITS;
pInfo->modify = 0;
pInfo->result = ER_SUCCESS;
}
else
pInfo->result = ER_SYSERR;
::ReleaseDC(hWnd, hDC);
if (pInfo->fpProgress) // 结束进度条,此调用不再支持用户中断
(*pInfo->fpProgress)(RWPROGRESSSIZE, RWPROGRESSSIZE);
}
// 操作命令解释函数---解释:CMD_SAVE_TO_FILE命令
// 将数据保存到指定文件中
void _fnCMD_SAVE_TO_FILE(INFOSTR *pInfo)
{
// 无法支持
pInfo->result = ER_NOTSUPPORT;
}
// 操作命令解释函数---解释:CMD_RESIZE命令
// 重新获取指定尺寸的图像位数据(只适用于矢量图像)
void _fnCMD_RESIZE(INFOSTR *pInfo)
{
// 指定尺寸在pInfo->sDIBInfo.bmi中指定
_fnCMD_LOAD_FROM_FILE(pInfo);
}
/*************************************************************************
*
* IsFileExist()
*
* 参数说明:
*
* char *lpFileName - 待判断的文件路径和名称(文件名)
*
* 返回值:
*
* BOOL - 如果指定的文件存在返回TRUE,否则返回FALSE。
*
* 描述:
*
* 判断指定的文件是否存在
*
* 该文件必需可以被读和写
*
************************************************************************/
BOOL IsFileExist(char *lpFileName)
{
CFile file;
BOOL bExist = FALSE; // 文件存在是TRUE,不存在是FALSE
// 确定指定的文件是否存在
if (file.Open(lpFileName, CFile::modeReadWrite))
{
bExist = TRUE;
file.Close();
}
return bExist;
}