WinCE平台下BMP转JPG代码备份
这大概是一年前做的事情了,当时的项目要求在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; } } } } |
虽然说今后可能再也不会碰这些东西了,但这毕竟是自己几个月的心血,所以还是贴下来吧,里面涉及的知识点太多了,今后自己有可能还有些参考价值的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库