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

  

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

posted @   一点一滴的Beer  阅读(2772)  评论(5编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
点击右上角即可分享
微信分享提示