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;
            }

         
        }
    }
}

  

  虽然说今后可能再也不会碰这些东西了,但这毕竟是自己几个月的心血,所以还是贴下来吧,里面涉及的知识点太多了,今后自己有可能还有些参考价值的。

原文地址:https://www.cnblogs.com/beer/p/2163219.html