这大概是一年前做的事情了,当时的项目要求在WinCE平台下BMP转JPG,然后自己折腾了好几个月才终于搞定,现在时间过去了快一年了,估计自己今后再也不会碰WinCE相关的东西了吧,而且也准备把相关的学习笔记和代码项目全部删除掉。这些没有经过整理过的东西,放在电脑上也是垃圾,还不如整理一下,放到网上,让有需要的同学借鉴参考一下吧。
开发环境:VS2005
开发平台:EPC6960 WinCE开发板
主要目标:在WinCE平台下完成BMP图片转JPG
实现方法:使用C++编写转换的DLL文件,使用C#编写界面,然后调用DLL
代码涉及知识点:
1.DLL的创建。
2.函数传入参数,传出参数。
3.位图格式。
4.位图的位运算及格式转换。
5.文件操作。
6.DLL的调用
7.……
一、图片格式转换的DLL项目
//****************************
//**WinCeCppCamDll项目
//**本项目中引用了 开发板公司提供的摄像头驱动DLL文件一个
//****************************
1.1导入和引用DLL中的参数
EpcsCam.h
#pragma once /* * 对应CAM_IOCTL_SAMSUNG_CAM_PR,打开RGB通道后,从uiRGB_Addr中获取视频图像数据,注意访问uiRGB_Addr时, * 必须采用内核模式(kernel mode),使用函数 SetKMode(TRUE),并在读取uiRGB_Addr后设置flag = 0, 如果有下 * 一帧数据来时,底层会将flag设为1,并设置uiRGB_Addr。这样方便读取每一帧数据 */ typedef struct __PINGPONG_PR { unsigned int uiRGB_Addr; unsigned char flag; /* 为1时候,视频数据有效 */ } PINGPONG_PR; /* * 对应CAM_IOCTL_SAMSUNG_CAM,打开YUV通道后,从uiY_Addr, uiCb_Addr, uiCr_Addr中获取视频图像数据,注意访 * 问三个地址时,必须采用内核模式(kernel mode),使用函数 SetKMode(TRUE),并在读取地址数据后设置flag = 0, * 如果有下一帧数据来时,底层会将flag设为1,并设置YUV三个地址值。这样方便读取每一帧数据 */ typedef struct PINGPONG { unsigned int uiY_Addr; unsigned int uiCb_Addr; unsigned int uiCr_Addr; unsigned char flag; } PINGPONG; /* * 此结构体用于设置视频输出图像的大小,视频输出包含两个通道:RGB通道和YUV通道,其中RGB通道为RGB565数据 * 格式,视频预览的时候使用RGB通道 */ typedef struct __IMAGE_SIZE { DWORD dwRGB_Width; /* RGB 通道的输出图像的宽度 */ DWORD dwRGB_Height; /* RGB 通道的输出图像的高度 */ DWORD dwYUV_Width; /* YUV 通道的输出图像的宽度 */ DWORD dwYUV_Height; /* YUV 通道的输出图像的高度 */ DWORD dwHorOffset; /* 视频源的水平剪切偏移 */ DWORD dwVerOffset; /* 视频源的垂直剪切偏移 */ } IMAGE_SIZE; typedef BOOL(*pEpcCamCapture)(BOOL bIsRGB, BOOL bIsYUV); typedef BOOL(*pEpcCamPreviewOn)(DWORD dwXSize, DWORD dwYSize); typedef BOOL(*pEpcCamSetImage)(IMAGE_SIZE* pImageSize); typedef BOOL(*pEpcCamGetRgbFrame)(PINGPONG_PR *prAddInfo); class EpcsCam { public: EpcsCam(void); public: ~EpcsCam(void); public: HINSTANCE hDLL;//载入DLL的实例句柄 char *pBmpData; public: /********************************************************************************************************* ** Function name: epcCamCapture ** Descriptions: 本函数用于打开或者关闭Camera的视频捕获,如果bIsRGB和bIsYUV为FALSE即为关闭视频捕获, ** bIsRGB和bIsYUV其中任一个为TRUE,即为打开视频捕获 ** input parameters: bIsRGB 为TRUE时候打开RGB通道,为FALSE的时候关闭RGB通道 ** bIsYUV 为TRUE时候打开YUV通道,为FALSE的时候关闭YUV通道 ** output parameters: 无 ** Returned value: TRUE:成功;FALSE:失败 *********************************************************************************************************/ /********************************************************************************************************* ** Function name: epcCamPreviewOn ** Descriptions: 本函数用于启动预览图像,当启动视频捕获(打开RGB通道)后, 即可看到图像显示效果 ** 建议启动预览时,设置图像的分辨率小于显示屏的分辨率 ** 注意,有以下情况将操作失败:1、全屏模式下,2、RGB通道图像设置值大于360*288个象素 ** input parameters: dwXSize: 预览图像的X坐标(以LCD的左上角为原点,可以为负值) ** dwYSize: 预览图像的Y坐标(以LCD的左上角为原点,可以为负值) ** output parameters: 无 ** Returned value: TRUE:成功;FALSE:失败 *********************************************************************************************************/ /********************************************************************************************************* ** Function name: epcCamSetImage ** Descriptions: 本函数用于设置Camera输出图像的大小, 包含RGB通道和YUV通道的视频输出大小 ** 打开该接口驱动后,RGB和YUV图像大小默认为320*240 ** 注意,有以下情况将操作失败:1、正在视频捕获,2、打开预览,3,正在全屏模式 ** input parameters: pImageSize: 用于设置两个通道的视频输出大小 ** output parameters: 无 ** Returned value: TRUE:成功;FALSE:失败 *********************************************************************************************************/ /********************************************************************************************************* ** Function name: epcCamGetRgbFrame ** Descriptions: 本函数用于获取RGB通道的图像的数据缓存区地址 ** input parameters: prAddInfo 存放获取的地址,注意访问该地址的图像数据时候使用SetKMode(TRUE) ** output parameters: 无 ** Returned value: TRUE:成功;FALSE:失败 *********************************************************************************************************/ BOOL epcCamCapture (BOOL bIsRGB, BOOL bIsYUV); BOOL epcCamPreviewOn (DWORD dwXSize, DWORD dwYSize); BOOL epcCamSetImage (IMAGE_SIZE* pImageSize); BOOL epcCamGetRgbFrame (PINGPONG_PR *prAddInfo); };
EpcsCam.cpp
#include "StdAfx.h" #include "EpcsCam.h" /********************************************************************************************************* ** Function name: epcCamCapture ** Descriptions: 本函数用于打开或者关闭Camera的视频捕获,如果bIsRGB和bIsYUV为FALSE即为关闭视频捕获, ** bIsRGB和bIsYUV其中任一个为TRUE,即为打开视频捕获 ** input parameters: bIsRGB 为TRUE时候打开RGB通道,为FALSE的时候关闭RGB通道 ** bIsYUV 为TRUE时候打开YUV通道,为FALSE的时候关闭YUV通道 ** output parameters: 无 ** Returned value: TRUE:成功;FALSE:失败 *********************************************************************************************************/ /********************************************************************************************************* ** Function name: epcCamPreviewOn ** Descriptions: 本函数用于启动预览图像,当启动视频捕获(打开RGB通道)后, 即可看到图像显示效果 ** 建议启动预览时,设置图像的分辨率小于显示屏的分辨率 ** 注意,有以下情况将操作失败:1、全屏模式下,2、RGB通道图像设置值大于360*288个象素 ** input parameters: dwXSize: 预览图像的X坐标(以LCD的左上角为原点,可以为负值) ** dwYSize: 预览图像的Y坐标(以LCD的左上角为原点,可以为负值) ** output parameters: 无 ** Returned value: TRUE:成功;FALSE:失败 *********************************************************************************************************/ /********************************************************************************************************* ** Function name: epcCamSetImage ** Descriptions: 本函数用于设置Camera输出图像的大小, 包含RGB通道和YUV通道的视频输出大小 ** 打开该接口驱动后,RGB和YUV图像大小默认为320*240 ** 注意,有以下情况将操作失败:1、正在视频捕获,2、打开预览,3,正在全屏模式 ** input parameters: pImageSize: 用于设置两个通道的视频输出大小 ** output parameters: 无 ** Returned value: TRUE:成功;FALSE:失败 *********************************************************************************************************/ /********************************************************************************************************* ** Function name: epcCamGetRgbFrame ** Descriptions: 本函数用于获取RGB通道的图像的数据缓存区地址 ** input parameters: prAddInfo 存放获取的地址,注意访问该地址的图像数据时候使用SetKMode(TRUE) ** output parameters: 无 ** Returned value: TRUE:成功;FALSE:失败 *********************************************************************************************************/ EpcsCam::EpcsCam(void) { hDLL=LoadLibrary(CString("\\FlashDisk2\\epcCameraLib.dll"));//加载动态链接库MyDll.dll文件; } EpcsCam::~EpcsCam(void) { FreeLibrary(hDLL);//卸载MyDll.dll文件; } BOOL EpcsCam::epcCamCapture (BOOL bIsRGB, BOOL bIsYUV) { BOOL bCaptureSucced=FALSE; pEpcCamCapture epcCamCapture =NULL; epcCamCapture=(pEpcCamCapture)GetProcAddress(hDLL,CString("epcCamCapture")); if (epcCamCapture) { bCaptureSucced=epcCamCapture(bIsRGB,bIsYUV); } return bCaptureSucced; } BOOL EpcsCam::epcCamPreviewOn (DWORD dwXSize, DWORD dwYSize) { BOOL bPreviewOnSucced=FALSE; pEpcCamPreviewOn epcCamPreviewOn =NULL; epcCamPreviewOn=(pEpcCamPreviewOn)GetProcAddress(hDLL,CString("epcCamPreviewOn")); if (epcCamPreviewOn) { bPreviewOnSucced=epcCamPreviewOn(dwXSize,dwYSize); } return bPreviewOnSucced; } BOOL EpcsCam::epcCamSetImage (IMAGE_SIZE* pImageSize) { BOOL bSetImageSucced=FALSE; pEpcCamSetImage epcCamSetImage =NULL; epcCamSetImage=(pEpcCamSetImage)GetProcAddress(hDLL,CString("epcCamSetImage")); if (epcCamSetImage) { bSetImageSucced=epcCamSetImage(pImageSize); } return bSetImageSucced; } BOOL EpcsCam::epcCamGetRgbFrame (PINGPONG_PR *prAddInfo) { BOOL betRgbFrameSucced=FALSE; pEpcCamGetRgbFrame epcCamGetRgbFrame =NULL; epcCamGetRgbFrame=(pEpcCamGetRgbFrame)GetProcAddress(hDLL,CString("epcCamGetRgbFrame")); if (epcCamGetRgbFrame) { betRgbFrameSucced=epcCamGetRgbFrame(prAddInfo); } return betRgbFrameSucced; }
1.2保存位图和保存异常日志等文件操作
FileOperate.h
#pragma once class FileOperate { public: FileOperate(void); ~FileOperate(void); public: static void WriteLogMsg(char chLogMsg[]); static CString GetTimeTag(); #if 1 static void WriteBin(char chBin[]); //根据数据保存图片 static BOOL bmpSaveImage (PTSTR pstrFileName, BITMAPFILEHEADER * pbmfh); // 保存位图 static void SaveBitMap(void); static CString SaveBmp(char *pcBmpData,char *bmpFileData); static CString SaveBmp0(BYTE *pcBmpData);//C++调用的函数 #endif public: static void ImageConvertDemo( BYTE *pInBmp565Data,//输入的RGB565位图的数据实体部分(不包括文件头等信息) DWORD dwBitMapDataSize,//位图数据实体长度(不包括文件头等信息) BYTE **ppOutMallocData,//传出的JPG图片数据实体的指针 DWORD * pdwOutJpegMemSize,//传出的JPG图片数据的大小 int * pState //状态码:记录在执行此函数的过程中可能出现的问题 //char *bmpFileData ); };
FileOperate.cpp
#include "StdAfx.h" #include "FileOperate.h" //#include "epccameralib.h" #include "initguid.h "//如果不引用此头文件,就会出现 无法解析外部符号的错误 #include "IImageDemo.h"//图片转码测试 FileOperate::FileOperate(void) { } FileOperate::~FileOperate(void) { } void FileOperate::WriteLogMsg(char chLogMsg[]) { char strFilePath[40] = "\\FlashDisk2\\Log\\";//如果是"\\Log\\"则到了当前盘符的根目录下了。 char strTimeFileName[20];//将当前时间转换成字符串---声明字符串长度的时候,要比实际长度多1,作为结尾符号 SYSTEMTIME sysTime; GetLocalTime( &sysTime ); //得到系统时间 //sprintf(strTimeFileName,"%d-%d-%d",sysTime.wYear,sysTime.wMonth,sysTime.wDay);//"2010-09-21" strcpy(strTimeFileName,"ErrorLog"); strcat(strTimeFileName,".txt");//加上扩展名--登录日志 strcat(strFilePath,strTimeFileName);//得到完整的路径名 FILE *fp;//文件指针 if ((fp=fopen(strFilePath,"a"))==NULL)//以追加的形式往文件中写东西 { //如果打开不成功,则一般表示没有Log目录 //创建Log目录,然后再重新打开--一般情况下,如果目录存在的话,就不会创建成功的。 if(!CreateDirectory(_T("\\FlashDisk2\\Log"),NULL)) { //创建目录失败 //printf("Create Directory failed!\n"); return; }else { //printf("Create Directory succeed!\n");//cout << "OK" <<endl; if ((fp=fopen(strFilePath,"a"))==NULL)//以追加的形式往文本文件中写东西 { //printf("Open Failed\n"); //exit(0); return; } } } char strTimeTag[30];//="2010-09-21"; //将时间转成字符串 sprintf(strTimeTag,"%d-%d-%d %d:%d:%d ",sysTime.wYear,sysTime.wMonth,sysTime.wDay, sysTime.wHour,sysTime.wMinute,sysTime.wSecond);//"2010-09-21" //strftime(chTimeTag, sizeof(chTimeTag), "%Y/%m/%d %X",&tim);//年月日时间字符串--作为登录日志中信息的时间标记头 fputs(strTimeTag,fp);//写入时间标记 fputs("# ",fp);//分隔符号 fputs(chLogMsg,fp);//写入消息日志 fputs("\n",fp);//换行 int i=fclose(fp); if (i==0) { //printf("succeed!\n"); }else { //printf("fail!\n"); } } CString FileOperate::GetTimeTag() { CString strTimetag; SYSTEMTIME sysTime; GetLocalTime( &sysTime ); //得到系统时间 strTimetag.Format(_T("%d%d%d-%d%d%d"),sysTime.wYear,sysTime.wMonth,sysTime.wDay,sysTime.wHour,sysTime.wMinute,sysTime.wSecond); //sprintf(strTimeFileName,"%d-%d-%d",sysTime.wYear,sysTime.wMonth,sysTime.wDay);//"2010-09-21" return strTimetag; } #if 1 void FileOperate::WriteBin(char chBin[]) { char strFilePath[40] = "\\FlashDisk2\\Bins\\";//如果是"\\Log\\"则到了当前盘符的根目录下了。 char strTimeFileName[20];//将当前时间转换成字符串---声明字符串长度的时候,要比实际长度多1,作为结尾符号 SYSTEMTIME sysTime; GetLocalTime( &sysTime ); //得到系统时间 //sprintf(strTimeFileName,"%d-%d-%d",sysTime.wYear,sysTime.wMonth,sysTime.wDay);//"2010-09-21" sprintf(strTimeFileName,"%d%d%d-%d%d%d",sysTime.wYear,sysTime.wMonth,sysTime.wDay, sysTime.wHour,sysTime.wMinute,sysTime.wSecond);//"2010-09-21" strcat(strTimeFileName,".bins");//加上扩展名--登录日志 strcat(strFilePath,strTimeFileName);//得到完整的路径名 FILE *fp;//文件指针 if ((fp=fopen(strFilePath,"wb+"))==NULL)//以追加的形式往二进制文件中写东西 { //如果打开不成功,则一般表示没有Log目录 //创建Log目录,然后再重新打开--一般情况下,如果目录存在的话,就不会创建成功的。 if(!CreateDirectory(_T("\\FlashDisk2\\Bins"),NULL)) { printf("Create Directory failed!\n"); }else { printf("Create Directory succeed!\n");//cout << "OK" <<endl; if ((fp=fopen(strFilePath,"a"))==NULL)//以追加的形式往文件中写东西 { printf("Open Failed\n"); exit(0); } } } char strTimeTag[30];//="2010-09-21"; //将时间转成字符串 sprintf(strTimeTag,"%d-%d-%d %d:%d:%d ",sysTime.wYear,sysTime.wMonth,sysTime.wDay, sysTime.wHour,sysTime.wMinute,sysTime.wSecond);//"2010-09-21" //strftime(chTimeTag, sizeof(chTimeTag), "%Y/%m/%d %X",&tim);//年月日时间字符串--作为登录日志中信息的时间标记头 //fputs(strTimeTag,fp);//写入时间标记 //fputs(" : ",fp);//分隔符号 //fputs(chLogMsg,fp);//写入消息日志 //fputs("\n",fp);//换行 fputs(chBin,fp); int i=fclose(fp); if (i==0) { printf("succeed!\n"); }else { printf("fail!\n"); } } // 保存位图--最原来的模型 void FileOperate::SaveBitMap(void) { // TODO: Add your control notification handler code here IMAGE_SIZE tDispSize = {0}; DWORD dwPreMode; PINGPONG_PR DataAddr; BITMAPFILEHEADER *pFileHead = NULL; /* 位图文件的头指针 */ BITMAPINFO *pBmpInfo = NULL; /* 位图信息的指针 */ char *pcBmpData = NULL; /* 位图数据区的指针 */ DWORD dwImgeX; /* 位图水平像素 */ DWORD dwImgeY; /* 位图垂直像素 */ DWORD dwFileHeadSize = sizeof(BITMAPFILEHEADER); /* 位图文件的头区域大小 */ DWORD dwInfoSize = sizeof(BITMAPINFO) + 4 * 2; /* 位图文件的信息区大小 */ DWORD dwBipMapSize; /* 位图文件的数据区大小 */ CString cstrPathname; cstrPathname="\\test.bmp"; dwImgeX = 320; dwImgeY = 240; dwBipMapSize = 2 * dwImgeX * dwImgeY; /* 文件头指针指向整个位图的空间 320*240*2/1024 =150K*/ pFileHead = (BITMAPFILEHEADER*)malloc(dwFileHeadSize + dwInfoSize + dwBipMapSize); pBmpInfo = (BITMAPINFO *)malloc(dwInfoSize); pFileHead->bfOffBits = dwFileHeadSize + dwInfoSize; /* 以下为填充位图的空间 */ pFileHead->bfSize = dwFileHeadSize + dwInfoSize + dwBipMapSize; pFileHead->bfType = 0x4D42; pcBmpData = (char *) pFileHead + pFileHead->bfOffBits; pBmpInfo->bmiHeader.biHeight = 0 - (signed)dwImgeY; pBmpInfo->bmiHeader.biWidth = dwImgeX ; pBmpInfo->bmiHeader.biBitCount = 16; pBmpInfo->bmiHeader.biClrImportant = 0; pBmpInfo->bmiHeader.biClrUsed = 0; pBmpInfo->bmiHeader.biCompression = BI_BITFIELDS; //pBmpInfo->bmiHeader.biCompression = BI_RGB; pBmpInfo->bmiHeader.biPlanes = 1; pBmpInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); pBmpInfo->bmiHeader.biSizeImage = dwBipMapSize; pBmpInfo->bmiColors[0].rgbBlue = 0x00; pBmpInfo->bmiColors[0].rgbGreen = 0xF8; pBmpInfo->bmiColors[0].rgbRed = 0x00; pBmpInfo->bmiColors[0].rgbReserved = 0x00; pBmpInfo->bmiColors[1].rgbBlue = 0xE0; pBmpInfo->bmiColors[1].rgbGreen = 0x07; pBmpInfo->bmiColors[1].rgbRed = 0x00; pBmpInfo->bmiColors[1].rgbReserved = 0x00; pBmpInfo->bmiColors[2].rgbBlue = 0x1F; pBmpInfo->bmiColors[2].rgbGreen = 0x00; pBmpInfo->bmiColors[2].rgbRed = 0x00; pBmpInfo->bmiColors[2].rgbReserved = 0x00; memcpy((void*)(pFileHead + 1), (void*)pBmpInfo, dwInfoSize); //最后将RGB565的图片数据全部COPY到pcBmpData中了----这里可以通过读文件的形式将这些数据读上来。!!!!!! //只需要在此处将那个RGB565的文件用二进制的格式读进来就OK了!!! CFile hFile; hFile.Open(_T("\\2010-9-23.bins"),CFile::modeRead); hFile.Read(pcBmpData,dwBipMapSize); bmpSaveImage((PTSTR)cstrPathname.GetBuffer(0), pFileHead); /* 保存成BMP图片 */ cstrPathname.ReleaseBuffer(); free(pFileHead); free(pBmpInfo); hFile.Close();//关闭文件 } //带参数的保存位图函数 BOOL FileOperate::bmpSaveImage(PTSTR pstrFileName, BITMAPFILEHEADER *pbmfh) { BOOL bSuccess ; DWORD dwBytesWritten ; HANDLE hFile; hFile = CreateFile ( pstrFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL) ; if (hFile == INVALID_HANDLE_VALUE) { return FALSE ; } bSuccess = WriteFile (hFile, pbmfh, pbmfh->bfSize, &dwBytesWritten, NULL); CloseHandle (hFile) ; if (!bSuccess || (dwBytesWritten != pbmfh->bfSize)) { DeleteFile (pstrFileName) ; return FALSE ; } return TRUE ; } //************************************ // Method: SaveBmp // FullName: FileOperate::SaveBmp // Access: public static // Returns: CString 位图的名称 // Qualifier: 保存位图 // Parameter: char * pcBmpDataTemp 位图数据区内容 //************************************ CString FileOperate::SaveBmp0(BYTE *pcBmpDataTemp) { // TODO: Add your control notification handler code here IMAGE_SIZE tDispSize = {0}; DWORD dwPreMode; PINGPONG_PR DataAddr; BITMAPFILEHEADER *pFileHead = NULL; /* 位图文件的头指针 */ BITMAPINFO *pBmpInfo = NULL; /* 位图信息的指针 */ char *pcBmpData = NULL; /* 位图数据区的指针 */ DWORD dwImgeX; /* 位图水平像素 */ DWORD dwImgeY; /* 位图垂直像素 */ DWORD dwFileHeadSize = sizeof(BITMAPFILEHEADER); /* 位图文件的头区域大小 */ DWORD dwInfoSize = sizeof(BITMAPINFO) + 4 * 2; /* 位图文件的信息区大小 */ DWORD dwBipMapSize; /* 位图文件的数据区大小 */ CString cstrPathname; cstrPathname+="\\FlashDisk2\\bmp\\"; cstrPathname+=GetTimeTag(); cstrPathname+=".bmp"; dwImgeX = 320; dwImgeY = 240; dwBipMapSize = 2 * dwImgeX * dwImgeY; /* 文件头指针指向整个位图的空间 320*240*2/1024 =150K*/ pFileHead = (BITMAPFILEHEADER*)malloc(dwFileHeadSize + dwInfoSize + dwBipMapSize); pBmpInfo = (BITMAPINFO *)malloc(dwInfoSize); pFileHead->bfOffBits = dwFileHeadSize + dwInfoSize; /* 以下为填充位图的空间 */ pFileHead->bfSize = dwFileHeadSize + dwInfoSize + dwBipMapSize; pFileHead->bfType = 0x4D42; pcBmpData = (char *) pFileHead + pFileHead->bfOffBits; pBmpInfo->bmiHeader.biHeight = 0 - (signed)dwImgeY; pBmpInfo->bmiHeader.biWidth = dwImgeX ; pBmpInfo->bmiHeader.biBitCount = 16; pBmpInfo->bmiHeader.biClrImportant = 0; pBmpInfo->bmiHeader.biClrUsed = 0; pBmpInfo->bmiHeader.biCompression = BI_BITFIELDS; pBmpInfo->bmiHeader.biPlanes = 1; pBmpInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); pBmpInfo->bmiHeader.biSizeImage = dwBipMapSize; pBmpInfo->bmiColors[0].rgbBlue = 0x00; pBmpInfo->bmiColors[0].rgbGreen = 0xF8; pBmpInfo->bmiColors[0].rgbRed = 0x00; pBmpInfo->bmiColors[0].rgbReserved = 0x00; pBmpInfo->bmiColors[1].rgbBlue = 0xE0; pBmpInfo->bmiColors[1].rgbGreen = 0x07; pBmpInfo->bmiColors[1].rgbRed = 0x00; pBmpInfo->bmiColors[1].rgbReserved = 0x00; pBmpInfo->bmiColors[2].rgbBlue = 0x1F; pBmpInfo->bmiColors[2].rgbGreen = 0x00; pBmpInfo->bmiColors[2].rgbRed = 0x00; pBmpInfo->bmiColors[2].rgbReserved = 0x00; memcpy((void*)(pFileHead + 1), (void*)pBmpInfo, dwInfoSize); //最后将RGB565的图片数据全部COPY到pcBmpData中了----这里可以通过读文件的形式将这些数据读上来。!!!!!! //只需要在此处将那个RGB565的文件用二进制的格式读进来就OK了!!! /*CFile hFile; hFile.Open(_T("\\2010-9-23.bins"),CFile::modeRead); hFile.Read(pcBmpData,dwBipMapSize);*/ memcpy(pcBmpData,pcBmpDataTemp,dwBipMapSize);//将图片数据区值COPY过来 // memcpy(bmpFileData,pFileHead,153666);//当程序运行到此处,C#程序中的临时数组已经有值了。 bmpSaveImage((PTSTR)cstrPathname.GetBuffer(0), pFileHead); /* 保存成BMP图片 */ cstrPathname.ReleaseBuffer(); free(pFileHead); free(pBmpInfo); return cstrPathname; } //************************************ // Method: SaveBmp // FullName: FileOperate::SaveBmp // Access: public static // Returns: CString 位图的名称 // Qualifier: 保存位图 // Parameter: char * pcBmpDataTemp 位图数据区内容 //************************************ CString FileOperate::SaveBmp(char *pcBmpDataTemp,char *bmpFileData) { // TODO: Add your control notification handler code here IMAGE_SIZE tDispSize = {0}; DWORD dwPreMode; PINGPONG_PR DataAddr; BITMAPFILEHEADER *pFileHead = NULL; /* 位图文件的头指针 */ BITMAPINFO *pBmpInfo = NULL; /* 位图信息的指针 */ char *pcBmpData = NULL; /* 位图数据区的指针 */ DWORD dwImgeX; /* 位图水平像素 */ DWORD dwImgeY; /* 位图垂直像素 */ DWORD dwFileHeadSize = sizeof(BITMAPFILEHEADER); /* 位图文件的头区域大小 */ DWORD dwInfoSize = sizeof(BITMAPINFO) + 4 * 2; /* 位图文件的信息区大小 */ DWORD dwBipMapSize; /* 位图文件的数据区大小 */ CString cstrPathname; cstrPathname+="\\FlashDisk2\\bmp\\"; cstrPathname+=GetTimeTag(); cstrPathname+=".bmp"; dwImgeX = 320; dwImgeY = 240; dwBipMapSize = 2 * dwImgeX * dwImgeY; /* 文件头指针指向整个位图的空间 320*240*2/1024 =150K*/ pFileHead = (BITMAPFILEHEADER*)malloc(dwFileHeadSize + dwInfoSize + dwBipMapSize); pBmpInfo = (BITMAPINFO *)malloc(dwInfoSize); pFileHead->bfOffBits = dwFileHeadSize + dwInfoSize; /* 以下为填充位图的空间 */ pFileHead->bfSize = dwFileHeadSize + dwInfoSize + dwBipMapSize; pFileHead->bfType = 0x4D42; pcBmpData = (char *) pFileHead + pFileHead->bfOffBits; pBmpInfo->bmiHeader.biHeight = 0 - (signed)dwImgeY; pBmpInfo->bmiHeader.biWidth = dwImgeX ; pBmpInfo->bmiHeader.biBitCount = 16; pBmpInfo->bmiHeader.biClrImportant = 0; pBmpInfo->bmiHeader.biClrUsed = 0; pBmpInfo->bmiHeader.biCompression = BI_BITFIELDS; pBmpInfo->bmiHeader.biPlanes = 1; pBmpInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); pBmpInfo->bmiHeader.biSizeImage = dwBipMapSize; pBmpInfo->bmiColors[0].rgbBlue = 0x00; pBmpInfo->bmiColors[0].rgbGreen = 0xF8; pBmpInfo->bmiColors[0].rgbRed = 0x00; pBmpInfo->bmiColors[0].rgbReserved = 0x00; pBmpInfo->bmiColors[1].rgbBlue = 0xE0; pBmpInfo->bmiColors[1].rgbGreen = 0x07; pBmpInfo->bmiColors[1].rgbRed = 0x00; pBmpInfo->bmiColors[1].rgbReserved = 0x00; pBmpInfo->bmiColors[2].rgbBlue = 0x1F; pBmpInfo->bmiColors[2].rgbGreen = 0x00; pBmpInfo->bmiColors[2].rgbRed = 0x00; pBmpInfo->bmiColors[2].rgbReserved = 0x00; memcpy((void*)(pFileHead + 1), (void*)pBmpInfo, dwInfoSize); //最后将RGB565的图片数据全部COPY到pcBmpData中了----这里可以通过读文件的形式将这些数据读上来。!!!!!! //只需要在此处将那个RGB565的文件用二进制的格式读进来就OK了!!! /*CFile hFile; hFile.Open(_T("\\2010-9-23.bins"),CFile::modeRead); hFile.Read(pcBmpData,dwBipMapSize);*/ memcpy(pcBmpData,pcBmpDataTemp,dwBipMapSize);//将图片数据区值COPY过来 memcpy(bmpFileData,pFileHead,153666);//当程序运行到此处,C#程序中的临时数组已经有值了。 bmpSaveImage((PTSTR)cstrPathname.GetBuffer(0), pFileHead); /* 保存成BMP图片 */ cstrPathname.ReleaseBuffer(); free(pFileHead); free(pBmpInfo); return cstrPathname; } #endif //pcBmpDataTemp--从摄像头中得到的565数据区内容 void FileOperate::ImageConvertDemo(BYTE *pInBmp565Data,//输入的RGB565位图的数据实体部分--不包括位图文件等信息 DWORD dwBitMapDataSize,//位图数据实体长度(不包括文件头等信息)153600 BYTE **ppOutMallocData,//传出的JPG图片数据实体 DWORD * pdwOutJpegMemSize,//传出的JPG图片数据的大小 int * pState //状态码:记录在执行此函数的过程中可能出现的问题 ) { BYTE * pOutRgb555BmpData=NULL;//输出的555格式的位图数据实体 DWORD dwRgb555BmpFileDataLength=0;//153666;//暂时先赋一个值,最终还是要通过传递得到的----###### dwRgb555BmpFileDataLength=sizeof(BITMAPFILEHEADER) //位图文件信息头:14 + sizeof(BITMAPINFOHEADER) //位图信息头:40 + 3*sizeof(RGBQUAD)//RGB掩码:12 + dwBitMapDataSize;//数据实体部分:153600 IImageDemo imgDemo; //FileOperate::SaveBmp0(pInBmp565Data);//测试代码:此处测试表明,可以取得到实时的数据了 imgDemo.ConvertBmpRgb565To555(pInBmp565Data,dwRgb555BmpFileDataLength,&pOutRgb555BmpData);//测试转码 BYTE * pJpegData=NULL; DWORD dwpJpegDataLength;//Jpeg数组的长度 imgDemo.ConvertRgb555BmpToJpgInMem(pOutRgb555BmpData,dwRgb555BmpFileDataLength,&pJpegData,&dwpJpegDataLength);//因为是在函数内部动态分配的内存,所以需要用指针的指针 //传出数据 *pdwOutJpegMemSize=dwpJpegDataLength;//传出长度---在最终代码中要简化 *ppOutMallocData=pJpegData; }
1.3转换图片格式
GetImage.h
#pragma once #include "initguid.h "//如果不引用此头文件,就会出现 无法解析外部符号的错误 #include "imaging.h"//图片转码测试 class GetImage { public: GetImage(DWORD dwRGB_Width,DWORD dwRGB_Height); GetImage(void); ~GetImage(void); public: DWORD dwRGB_Width; /* RGB 通道的输出图像的宽度 */ DWORD dwRGB_Height; /* RGB 通道的输出图像的高度 */ public: //转换图片格式,并得到jpeg文件的数组 void GetJpegBytes( BYTE *pInBmp565Data,//输入的RGB565位图的数据实体部分(不包括文件头等信息) DWORD dwBitMapDataSize,//位图数据实体长度(不包括文件头等信息) BYTE **ppOutMallocData,//传出的JPG图片数据实体的指针 DWORD * pdwOutJpegMemSize,//传出的JPG图片数据的大小 int * pState //状态码:记录在执行此函数的过程中可能出现的问题 ); private: //将Rgb565编码格式的位图转成Rgb555的位图 void ConvertBmpRgb565To555( BYTE * pInRgb565BmpData, //输入的565格式的位图数据实体 DWORD dwRgb555BmpFileDataLength,//位图文件大小 BYTE ** ppOutRgb555BmpData//输出的555格式的位图数据实体 ); //将数组转换到IStream中 void CopyByteArrayToISream( BYTE *pInByteArray,//输入的字节数组 DWORD dwArrayLength,//字节数组的长度 IStream **ppOutIStream//传出的由字节转换的流 ); /* *函数介绍:根据编码器类型名称,得到指定的编码器CLSID *入口参数:pImagingFactory: Image工厂接口对象 wszMimeType : Image编码格式名称 *出口参数:pclsid :编码器的CLSID *返回值:TRUE : 成功; FALSE: 失败 */ BOOL GetEnCodecCLSID(IImagingFactory * pImagingFactory, WCHAR * wszMimeType , CLSID * pclsid); //Rgb555编码的BMP位图转JPG--在内存中进行 void ConvertRgb555BmpToJpgInMem( BYTE * pInRgb555BmpFileData, //输入的RGB555位图文件流--包括位图数据实体及文件和位图信息 DWORD dwRgb555BmpFileDataLength,//RGB555位图文件流的长度 BYTE ** ppOutJpegData,//输出的JPG位图文件数据流 DWORD * dwpOutJpegDataLegth//转码后的JPG位图大小 ); };
GetImage.cpp
#include "StdAfx.h" #include "GetImage.h" #include "CamException.h" //#include "epccameralib.h"//摄像头驱动 GetImage::GetImage(void) { } GetImage::GetImage(DWORD dwWidth,DWORD dwHeight) { dwRGB_Height=dwHeight; dwRGB_Width=dwWidth; } GetImage::~GetImage(void) { } void GetImage::GetJpegBytes( BYTE *pInBmp565Data,//输入的RGB565位图的数据实体部分--不包括位图文件等信息 DWORD dwBitMapDataSize,//位图数据实体长度(不包括文件头等信息)153600 BYTE **ppOutMallocData,//传出的JPG图片数据实体 DWORD * pdwOutJpegMemSize,//传出的JPG图片数据的大小 int * pState //状态码:记录在执行此函数的过程中可能出现的问题 ) { try { BYTE * pOutRgb555BmpData=NULL;//输出的555格式的位图数据实体 DWORD dwRgb555BmpFileDataLength=0;//位图文件长度153666 dwRgb555BmpFileDataLength=sizeof(BITMAPFILEHEADER) //位图文件信息头:14 + sizeof(BITMAPINFOHEADER) //位图信息头:40 + 3*sizeof(RGBQUAD)//RGB掩码:12 + dwBitMapDataSize;//数据实体部分:153600 //将位图数据转码成555数据,并加上相关文件头,最后形成555位图文件 ConvertBmpRgb565To555(pInBmp565Data,dwRgb555BmpFileDataLength,&pOutRgb555BmpData); #pragma region //测试没有取到图片的情况 //CFile hSaveFile; //hSaveFile.Open(L"\\565bmp.bin",CFile::modeCreate | CFile::modeWrite |CFile::modeNoTruncate); ////创立一个txt文件。 //hSaveFile.SeekToEnd(); //文件末尾 //hSaveFile.Write(pInBmp565Data,dwBitMapDataSize); //hSaveFile.Close(); #pragma endregion if (pOutRgb555BmpData==NULL) { throw CString("ConvertBmpRgb565To555位图图片格式转码失败"); } BYTE * pJpegData=NULL; DWORD dwpJpegDataLength;//Jpeg数组的长度 ConvertRgb555BmpToJpgInMem(pOutRgb555BmpData,dwRgb555BmpFileDataLength,&pJpegData,&dwpJpegDataLength); //因为是在函数内部动态分配的内存,所以需要用指针的指针 if (pOutRgb555BmpData!=NULL) { free(pOutRgb555BmpData);//555位图数据使用完毕后,就释放 pOutRgb555BmpData=NULL; } if (pJpegData==NULL) { throw CString("ConvertRgb555BmpToJpgInMem位图压缩失败"); } //传出数据 *pdwOutJpegMemSize=dwpJpegDataLength;//传出长度---在最终代码中要简化 *ppOutMallocData=pJpegData; } catch(CString exMsg) { exMsg=L"GetJpegBytes(BYTE*,DWORD,BYTE**,DWORD*,int*):" + exMsg; CamException::WriteToFile(exMsg); } catch (CException* e) { TCHAR szCause[255]; e->GetErrorMessage(szCause, 255); CString exMsg=CString(szCause); exMsg=L"GetJpegBytes(BYTE*,DWORD,BYTE**,DWORD*,int*):" + exMsg; CamException::WriteToFile(exMsg); } } //将Rgb565编码格式的位图转成Rgb555的位图---位图的大小不会变化,只是数据的编码方式发生变化 void GetImage::ConvertBmpRgb565To555( BYTE * pInRgb565BmpData,//输入的565格式的位图数据实体----不包括位图文件信息 DWORD dwRgb555BmpFileDataLength,//位图文件大小153666 BYTE ** ppOutRgb555BmpFileData//输出的555格式的位图文件数据流--可以形成完整文件 ) { try { #pragma region //设置位图文件 BITMAPFILEHEADER *pFileHead = NULL; /* 位图文件的头指针 */ BITMAPINFO *pBmpInfo = NULL; /* 位图信息的指针 */ char *pcBmpData = NULL; /* 位图数据区的指针 */ DWORD dwImgeX; /* 位图水平像素 */ DWORD dwImgeY; /* 位图垂直像素 */ DWORD dwFileHeadSize = sizeof(BITMAPFILEHEADER); /* 位图文件的头区域大小 */ DWORD dwInfoSize = sizeof(BITMAPINFO) + 4 * 2; /* 位图文件的信息区大小 */ DWORD dwBipMapSize; /* 位图文件的数据区大小 */ dwBipMapSize = 2 * dwRGB_Height * dwRGB_Width; //文件头指针指向整个位图的空间 320*240*2/1024 =150K pFileHead = (BITMAPFILEHEADER*)malloc(dwFileHeadSize + dwInfoSize + dwBipMapSize); if (pFileHead==NULL) { throw CString("pFileHead位图信息头内存分配失败"); } pBmpInfo = (BITMAPINFO *)malloc(dwInfoSize); if (pBmpInfo==NULL) { free(pFileHead); pFileHead==NULL;//释放已经申请到的内存 throw CString("pBmpInfo位图信息头内存分配失败"); } pFileHead->bfOffBits = dwFileHeadSize + dwInfoSize; /* 以下为填充位图的空间 */ pFileHead->bfSize = dwFileHeadSize + dwInfoSize + dwBipMapSize; pFileHead->bfType = 0x4D42;//位图文件的 类型代码 pcBmpData = (char *) pFileHead + pFileHead->bfOffBits; pBmpInfo->bmiHeader.biHeight = 0 - (signed)dwRGB_Height; pBmpInfo->bmiHeader.biWidth = dwRGB_Width ; pBmpInfo->bmiHeader.biBitCount = 16; pBmpInfo->bmiHeader.biClrImportant = 0; pBmpInfo->bmiHeader.biClrUsed = 0; //pBmpInfo->bmiHeader.biCompression = BI_BITFIELDS;//RGB565格式 pBmpInfo->bmiHeader.biCompression = BI_RGB;//RGB555格式 pBmpInfo->bmiHeader.biPlanes = 1; pBmpInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); pBmpInfo->bmiHeader.biSizeImage = dwBipMapSize; memcpy((void*)(pFileHead + 1), (void*)pBmpInfo, dwInfoSize); memcpy(pcBmpData,pInRgb565BmpData,dwBipMapSize);//将摄像头数据复制到位图文件内存缓冲区中 #pragma endregion #pragma region //进行颜色分量提取,并转码成RGB555 char * p555Data=NULL; p555Data=(char*)malloc(dwBipMapSize);//申请一片数据作为555数据的缓冲区 if (p555Data==NULL) { free(pFileHead); pFileHead=NULL; free(pBmpInfo); pBmpInfo=NULL; throw CString("p555Data内存分配失败"); } DWORD width=dwRGB_Width;//320 DWORD height=dwRGB_Height;//240 int pitch=width+width%2;//偏移量 for (int i=0;i<height;i++)//图片的高度是240 { for (int j=0;j<width;j++) { //分解出RGB三分量---RGB565的 UCHAR b=pcBmpData[(i*pitch+j)*2]&0x1F; UCHAR g=((((pcBmpData[(i*pitch+j)*2+1]<<5)&0xFF)>>2) & 0x38) +((pcBmpData[(i*pitch+j)*2]>>5)&0x07); UCHAR r=(pcBmpData[(i*pitch+j)*2+1]>>3)&0x1F; g=g/2;//把g分量从RGB565标准转码成RGB555标准 //将新的RGB分量弄到RGB555的图片数据区中. p555Data[(i*pitch+j)*2] = ((g<<5)&0xE0)+b;//gb分量 p555Data[(i*pitch+j)*2+1] = (r<<2)+(g/8);//rg分量 } } memcpy(pcBmpData,p555Data,dwBipMapSize);//将新的数据区内容复制到原来的数据区中进行了数据覆盖 #pragma endregion //---*****传出参数 *ppOutRgb555BmpFileData=(BYTE *)malloc(dwRgb555BmpFileDataLength); if (*ppOutRgb555BmpFileData==NULL) { free(pFileHead); pFileHead=NULL; free(pBmpInfo); pBmpInfo=NULL; free(p555Data); p555Data=NULL; throw CString("*ppOutRgb555BmpFileData内存分配失败"); } memcpy(*ppOutRgb555BmpFileData,pFileHead,dwRgb555BmpFileDataLength); free(pFileHead); free(pBmpInfo); free(p555Data); } catch(CString exMsg) { exMsg=L"ConvertBmpRgb565To555(BYTE*,DWORD,BYTE**):" + exMsg; CamException::WriteToFile(exMsg); } catch (CException* e) { TCHAR szCause[255]; e->GetErrorMessage(szCause, 255); CString exMsg=CString(szCause); exMsg=L"ConvertBmpRgb565To555(BYTE*,DWORD,BYTE**):" + exMsg; CamException::WriteToFile(exMsg); } } // //Rgb555编码的BMP位图转JPG--在内存中进行 void GetImage::ConvertRgb555BmpToJpgInMem( BYTE * pInRgb555BmpFileData, //输入的RGB555位图文件流--包括位图数据实体及文件和位图信息 DWORD dwRgb555BmpFileDataLength,//RGB555位图文件流的长度 BYTE ** ppOutJpegData,//传出的JPG文件数据流 DWORD * dwpOutJpegDataLegth//JPG文件流大小 ) { try { #pragma region HRESULT hr;//保存每个步骤的中间结果,判断过程运行是否正确----到时候有必要写个异常日志记录 TCHAR *tszMime;//输出图片格式 tszMime = L"image/jpeg"; //指定转换后,图象文件的格式 IStream *pRgb555BmpStream = NULL; // 流接口对象---读取BMP文件,然后在内存中保存此文件数据 IStream * pJpegStream=NULL;//用来保存转换的JPG文件 IImagingFactory * pImagingFactory = NULL ; //Image工厂接口对象 IImageSink *pImageSink = NULL; //Image Sink接口对象 IImageDecoder *pImageDecoder = NULL; //解码器接口对象 IImageEncoder *pImageEncoder = NULL; //编码器接口对象 CLSID clsidEncoder; //编码器CLSID //小技巧:有些变量虽然只在函数体里局部用到,但是因为是动态分配的内存,需要最后手动释放内存,最好放在最前面声明,防止最后遗忘了。 STATSTG * pIStreamState=NULL;//得到pJpegStream的状态 BYTE * pJpegData=NULL;//用来存储从文件流中剥出来的数据。 //初始化COM环境 if (FAILED(hr = CoInitializeEx(NULL, COINIT_MULTITHREADED))) { TRACE(L"COINIT_MULTITHREADED ERROR"); return; } CopyByteArrayToISream(pInRgb555BmpFileData,dwRgb555BmpFileDataLength,&pRgb555BmpStream);//承接数据 //将流指针移到流起点。-----一般都要进行一下这样的测试 LARGE_INTEGER dlibMove0; dlibMove0.HighPart=0; dlibMove0.LowPart=0; pRgb555BmpStream->Seek(dlibMove0,STREAM_SEEK_SET,NULL); //得到Image工厂接口对象---用指定的类标识符创建一个Com对象,用指定的类标识符创建一个未初始化的对象。 hr = CoCreateInstance(CLSID_ImagingFactory,//创建的Com对象的类标识符(CLSID) NULL,//指向接口IUnknown的指针 CLSCTX_INPROC_SERVER,//运行可执行代码的上下文 IID_IImagingFactory,//创建的Com对象的接口标识符 (void**) &pImagingFactory);//用来接收指向Com对象接口地址的指针变量 if (FAILED(hr)) { TRACE(L"IMAGE FACTORY CREATED ERROR"); goto finish; } //创建解码器接口 if (FAILED(hr = pImagingFactory->CreateImageDecoder(pRgb555BmpStream, DecoderInitFlagBuiltIn1st, &pImageDecoder))) { goto finish; } //根据编码器类型名称得到编码器CLSID if (!GetEnCodecCLSID(pImagingFactory,tszMime, &clsidEncoder ))//tszMime = L"image/jpeg"; //指定转换后,图象文件的格式 { goto finish; } if (FAILED(hr = CreateStreamOnHGlobal(NULL,TRUE,&pJpegStream)))//必需要和某个内存区域关联,或者进行一次实例化,比如用COleStreamFile { goto finish; } if (FAILED(hr = pImagingFactory->CreateImageEncoderToStream(&clsidEncoder, pJpegStream, &pImageEncoder))) { goto finish; } //得到编码器接口的sink对象。此ImageSink接口作为一个槽或者管道来理解; //是用于负责pImageEncoder和pImageDecoder之间的传输 if (FAILED(hr = pImageEncoder->GetEncodeSink(&pImageSink))) { goto finish; } //开始解码 if (FAILED(hr = pImageDecoder->BeginDecode(pImageSink, NULL))) { goto finish; } //循环解码,直到结束 for(;;)//for循环其实只运行了一个周期 { //解码 hr = pImageDecoder->Decode();//解码后,生成一个8K的文件 //继续解码后面的部分 if (E_PENDING == hr) { Sleep(500); } //失败 else if (FAILED(hr)) { //终止解码 pImageDecoder->EndDecode(hr); goto finish; } else { //解码成功 break; } } pImageDecoder->EndDecode(hr);//结束解码 pImageSink->Release();//释放pImageSink对象 pImageSink = NULL; pImageEncoder->TerminateEncoder();//结束编码,此时就已经完成了文件格式的转换 #pragma region //从流中提取数据到BYTE数组中 DWORD dwStreamLengthLowPart;//状态中的长度分量--低位(因为实际图片数据不需要高位那么长) //得到pJpegStream的长度--然后提取出数据,保存到BYTE数组中 pIStreamState=(STATSTG *)malloc(sizeof(STATSTG));//如果不动态开辟空间,将无法传值进来。 if (NULL == pIStreamState)//如果申请内存没有成功 { CamException::WriteToFile(L"pIStreamState申请内存失败"); goto finish; } if (FAILED(hr=pJpegStream->Stat(pIStreamState,STATFLAG_NONAME))) { CamException::WriteToFile(L"pJpegStream获取状态失败"); goto finish; } dwStreamLengthLowPart = pIStreamState->cbSize.LowPart;//取出流状态中的长度分量 free(pIStreamState); pIStreamState=NULL;//指针置空,防止野指针出现 pJpegData = (BYTE *)malloc(dwStreamLengthLowPart);//用来存储从文件流中剥出来的数据。 if (NULL == pJpegData)//如果申请内存没有成功 { goto finish; } //将流指针移到流起点。 LARGE_INTEGER dlibMove; dlibMove.HighPart=0; dlibMove.LowPart=0; pJpegStream->Seek(dlibMove,STREAM_SEEK_SET,NULL); hr=pJpegStream->Read(pJpegData,dwStreamLengthLowPart,NULL);//将流文件内容放置到数据中 if (FAILED(hr)) { goto finish; } #pragma endregion *ppOutJpegData=pJpegData;//将图片数据指针传递出去 *dwpOutJpegDataLegth = dwStreamLengthLowPart;//此处传值可能出了点小故障,明天就干脆把这两个参数封装到一个自定义的结构里面,然后动态生成吧。 finish: //释放pRgb555BmpStream对象 if (pRgb555BmpStream) pRgb555BmpStream->Release(); if (pJpegStream) pJpegStream->Release(); //释放pImageSink对象 if (pImageSink) pImageSink->Release(); //释放pImageDecoder对象 if (pImageDecoder) pImageDecoder->Release(); //释放pImageEncoder对象 if (pImageEncoder) pImageEncoder->Release(); //释放IImagingFactory接口对象 if (pImagingFactory) pImagingFactory->Release(); //释放程序占用的COM资源 CoUninitialize(); #pragma endregion } catch(CString exMsg) { exMsg=L"ConvertBmpRgb565To555(BYTE*,DWORD,BYTE**):" + exMsg; CamException::WriteToFile(exMsg); } catch (CException* e) { TCHAR szCause[255]; e->GetErrorMessage(szCause, 255); CString exMsg=CString(szCause); exMsg=L"ConvertBmpRgb565To555(BYTE*,DWORD,BYTE**):" + exMsg; CamException::WriteToFile(exMsg); } } void GetImage::CopyByteArrayToISream( BYTE *pInByteArray,//输入的字节数组 DWORD dwArrayLength,//字节数组的长度 IStream **ppOutIStream//传出的由字节转换的流 ) { try { HRESULT hrRet = S_FALSE; HGLOBAL hg = NULL; BYTE* pbLocked = NULL; //分配内存--此方法已经过时,现在一般都用malloc或者new了 hg = GlobalAlloc(GMEM_MOVEABLE, dwArrayLength); if (NULL == hg) { CamException::WriteToFile(L"hg分配内存失败"); goto error; } //得到已经分配的内存指针 pbLocked = (BYTE*) GlobalLock(hg); if (NULL == pbLocked) { CamException::WriteToFile(L"pbLocked获取指针失败"); goto error; } memcpy(pbLocked,pInByteArray,dwArrayLength);//不从文件中读取,而是直接在内存地址区间进行复制 GlobalUnlock(hg);//解锁已经分配全局内存,对应GlobalLock(hg) hrRet = CreateStreamOnHGlobal(hg, TRUE, ppOutIStream);//创建Stream对象 return; error: //错误处理,并释放内存(没有出现错误的话,不会出现在此处) if (pbLocked) GlobalUnlock(hg); if (hg) GlobalFree(hg); } catch(CString exMsg) { exMsg=L"CopyByteArrayToISream(BYTE*,DWORD,IStream **):" + exMsg; CamException::WriteToFile(exMsg); } catch (CException* e) { TCHAR szCause[255]; e->GetErrorMessage(szCause, 255); CString exMsg=CString(szCause); exMsg=L"CopyByteArrayToISream(BYTE*,DWORD,IStream **):" + exMsg; CamException::WriteToFile(exMsg); } } BOOL GetImage::GetEnCodecCLSID( IImagingFactory * pImagingFactory, WCHAR * wszMimeType , CLSID * pclsid ) { UINT uiCount; ImageCodecInfo * codecs; HRESULT hr; BOOL fRet = FALSE; //枚举系统已经安装的编码器 hr = pImagingFactory->GetInstalledEncoders(&uiCount, &codecs); //查找制定编码器的CLSID for (UINT i = 0; i < uiCount; i++) { if (wszMimeType && !wcscmp(wszMimeType, codecs[i].MimeType)) { *pclsid = codecs[i].Clsid; fRet = TRUE; break; } } //释放内存 CoTaskMemFree(codecs); // return fRet; }
截止上面已经完成了在内存当中对图片的转换了。
二、使用C#项目调用DLL
里面为了防止内存泄漏,专程让这个转换做了1000次,最后发现没有问题了。
using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices;//引入dll文件中的函数 // 添加新的命名空间。 using System.IO; //using System.Drawing.Imaging; //using System.Drawing; namespace WinCeCsUseDll { class Program { [DllImport("WinCeCppCamdll.dll", CharSet = CharSet.Auto)]//WinCE平台下,居然没有ANSI这个编码选项。 private static extern void GetCamShoot( int imgWidth,//图片宽度 int imgHeight,//图片高度 ref IntPtr ppOutMallocJpegData,//传出的JPG图片数据实体 ref int pdwOutJpegMemSize,//传出的JPG图片数据的大小 ref int pState //状态码:记录在执行此函数的过程中可能出现的问题 ); [DllImport("WinCeCppCamdll.dll", CharSet = CharSet.Auto)]//WinCE平台下,居然没有ANSI这个编码选项。 private static extern void FreeMemory(ref IntPtr intPtr); static void Main(string[] args) { try { #region 用C#承接C++的DLL开辟的内存空间中的数据 int imageWidth = 640; int imageHeight = 480; for (int i = 0; i < 10000; i++) { //下面再对内存区间进行传递 int memSize = 0; int intState = 0; IntPtr intPtr = new IntPtr(); GetCamShoot(imageWidth, imageHeight, ref intPtr, ref memSize, ref intState); ////因为采用 致远公司提供的驱动有点奇怪,每次捕捉的好像都是一一次内存中的东西 ////如果是第一次启动程序,那么会出现没有数据的情况。所以需要进行一次容错--再读一次数据 //if (intPtr.Equals(IntPtr.Zero)) //{ // // GetCamShoot(ref intPtr, ref memSize, ref intState); //} byte[] btTemp = new byte[memSize]; Marshal.Copy(intPtr, btTemp, 0, memSize); //将BYTE数组写成文件--测试代码 string path = "\\"; string SendFileName = "recvBmpData.jpg"; FileStream MyFileStream = new FileStream(path + SendFileName, FileMode.Create, FileAccess.Write); MyFileStream.Write(btTemp, 0, btTemp.Length); //将接收到的数据包写入到文件流对象 MyFileStream.Close();//关闭文件流 ////Marshal.FreeHGlobal(intPtr); FreeMemory(ref intPtr); ////Marshal.FreeCoTaskMem(intPtr);//free tha memory---用FreeHGlobal释放会出现错误,不知道这个函数是不是真正实现了释放。 ////intPtr = IntPtr.Zero; if (i == 9999) break; } #endregion }catch(Exception e) { int a = 3; } } } }
虽然说今后可能再也不会碰这些东西了,但这毕竟是自己几个月的心血,所以还是贴下来吧,里面涉及的知识点太多了,今后自己有可能还有些参考价值的。