[MFC/C++]通过套接字发送 CBitmap 的位,并在接收端重新构造它
[MFC/C++]Sending CBitmap's bits over socket, and re-construct it on receiver side
刚接触MFC,尝试通过一个基于VS2008的MFC对话框项目来学习。以下是我完成的存档:
首先,我设法将文件夹中的图片列表显示到列表框控件中。之后,我还处理了列表框每一行的点击事件,以将图片加载并显示到右侧的图片控件(位图类型)。您可以查看下图以便于理解:Please click here for the image of my MFC dialog
这是代码。注意m_ListCtrl
和static_picture
是列表框和图片控件的变量:
void CMyClientDlg::OnLbnSelchangeList1(){
CString imagePath;
m_ListCtrl.GetText(m_ListCtrl.GetCurSel(),imagePath);
CImage picture;
picture.Load(imagePath);
if (!picture.IsNull())
{
float screenWidth = 200, screenHeight = 200;
float imageWidth = picture.GetWidth();
float imageHeight = picture.GetHeight();
//scaling:
float pictureRatio = imageWidth/ imageHeight;
float newImageWidth;
float newImageHeight;
int aligmentX = 0;
int aligmentY = 0;
if (pictureRatio <= 1)
{
newImageWidth = imageWidth*(screenHeight/imageHeight);
newImageHeight = screenHeight;
aligmentX = (screenWidth-newImageWidth)/2;
}
else
{
newImageWidth = screenWidth;
newImageHeight = imageHeight*(screenWidth/imageWidth);
aligmentY = (screenHeight - newImageHeight)/2;
}
//end scaling.
CDC *screenDC = GetDC();
CDC mDC;
mDC.CreateCompatibleDC(screenDC);
CBitmap bitMap;
bitMap.CreateCompatibleBitmap(screenDC, screenWidth, screenHeight);
CBitmap *pob = mDC.SelectObject(&bitMap);
mDC.SetStretchBltMode(HALFTONE);
picture.StretchBlt(mDC.m_hDC, aligmentX, aligmentY, newImageWidth, newImageHeight, 0, 0, imageWidth, imageHeight, SRCCOPY);
mDC.SelectObject(pob);
/*.......code to convert bitmap to BYTE* ........*/
/*.......code to send BYTE* over socket........*/
//display the bit map
static_picture.SetBitmap((HBITMAP)bitMap.Detach());
//clean up
ReleaseDC(screenDC);
}
}
所以现在我想更进一步,并尝试使用套接字...是的,我成功地通过套接字发送和接收了简单的 char* 或 CString。
我想做的是:不是在此对话框上显示图片,而是在另一个对话框(服务器)上显示图像。
不知何故,我了解到有 2 个函数听起来很有效:SetBitmapBits()
和 GetBitmapBits()
(老实说,我只是在一些来源上阅读它,不知道它们是否适合我的目标)。
所以,我添加了这段代码,将上面的位图变成了 BYTE 数组 bmpBuffer
:
BITMAP bmpProperties;
bitMap.GetBitmap(&bmpProperties);
int bmpDemension = bmpProperties.bmWidthBytes*bmpProperties.bmHeight;
BYTE* bmpBuffer=(BYTE*)GlobalAlloc(GPTR, bmpDemension);
bitMap.GetBitmapBits(bmpDemension,bmpBuffer);
然后通过套接字发送该数组:
UpdateData(TRUE);
char *socketBuffer = reinterpret_cast<char*>(bmpBuffer);
send(m_ClientSocket, socketBuffer, sizeof(socketBuffer), 0);
//clean up after send
GlobalFree((HGLOBAL)bmpBuffer);
在另一个对话框中。注意:我已经将位图的尺寸硬编码为 160000,只是为了简化问题:
void CMyServer2Dlg::OnReceive(){
char *socketBuffer = new char [1025];
int iLen;
iLen = recv(m_sConnected, socketBuffer, 1025, NULL);
if(iLen==SOCKET_ERROR)
{
AfxMessageBox("Could not Receive");
}
else
{
BYTE* bmpBuffer = reinterpret_cast<BYTE*>(socketBuffer);
//re-construct the bitmap
CBitmap clone;
CDC *screenDC = GetDC();
CDC mDC;
mDC.CreateCompatibleDC(screenDC);
clone.CreateCompatibleBitmap(screenDC, 200, 200);
clone.SetBitmapBits(160000,bmpBuffer);
//Picture control(type bitmap) has variable "static_picture"
static_picture.SetBitmap((HBITMAP)clone.Detach());
UpdateData(FALSE);
ReleaseDC(screenDC);
GlobalFree((HGLOBAL)bmpBuffer);
}
delete socketBuffer;
而且,它根本不起作用...请告诉我我在哪里搞砸了?很抱歉 post.......
我觉得最大的可能是你的接收端没有获取到图片的所有数据。我建议您在发送时将位图的大小放入包中,以便接收方获得正确的大小。
这里有一些示例代码。请注意它们只是为了展示想法,您可能需要进行一些调试以确保它们有效。
第一步:打包位图大小。我想这里的大小小于 64K,所以使用了 int。如果大小可能大于 64k,您可能需要使用 INT64。
int bmpDemension = bmpProperties.bmWidthBytes*bmpProperties.bmHeight;
int bufferSize = bmpDemension + sizeof(int);
BYTE* bmpBuffer=(BYTE*)GlobalAlloc(GPTR, bufferSize );
bitMap.GetBitmapBits(bmpDemension,bmpBuffer + sizeof(int));
memcpy(bmpBuffer, &bmpDemension, sizeof(int)); // put the size into the head of package.
第 2 步:发送出去
请注意,我在这里使用 bufferSize,因为 sizeof(bmpBuffer) returns 指针大小,即 4,而不是 space 大小。
UpdateData(TRUE);
char *socketBuffer = reinterpret_cast<char*>(bmpBuffer);
send(m_ClientSocket, socketBuffer, bufferSize , 0);
//clean up after send
GlobalFree((HGLOBAL)bmpBuffer);
接收端:
首先,你读取位图的大小,然后根据数据的大小进行接收。
void CMyServer2Dlg::OnReceive(){
char socketBuffer[1025];
int iLen;
iLen = recv(m_sConnected, socketBuffer, sizeof(int), NULL); //read the bigmap size
if(iLen==SOCKET_ERROR)
{
AfxMessageBox("Could not Receive");
}
else
{
int dimension = *((int *) socketBuffer);
char * bitmapBuffer = new char[dimension];
int readSize = dimension;
char * pBuffer = bitmapBuffer;
while (readSize > 0)
{
int sizeToRead = readSize > sizeof(socketBuffer) ? sizeof(socketBuffer) : readSize;
iLen = recv(m_sConnected, socketBuffer, sizeToRead , NULL);
memcpy(pBuffer, socketBuffer, iLen);
pBuffer += iLen;
readSize -= iLen;
}
// when the loop done, you shall have all data in bitmapBuffer.
....
// I leave the remaining code to you.
同样,这些代码只是为了演示这个想法。
刚接触MFC,尝试通过一个基于VS2008的MFC对话框项目来学习。以下是我完成的存档:
首先,我设法将文件夹中的图片列表显示到列表框控件中。之后,我还处理了列表框每一行的点击事件,以将图片加载并显示到右侧的图片控件(位图类型)。您可以查看下图以便于理解:Please click here for the image of my MFC dialog
这是代码。注意m_ListCtrl
和static_picture
是列表框和图片控件的变量:
void CMyClientDlg::OnLbnSelchangeList1(){
CString imagePath;
m_ListCtrl.GetText(m_ListCtrl.GetCurSel(),imagePath);
CImage picture;
picture.Load(imagePath);
if (!picture.IsNull())
{
float screenWidth = 200, screenHeight = 200;
float imageWidth = picture.GetWidth();
float imageHeight = picture.GetHeight();
//scaling:
float pictureRatio = imageWidth/ imageHeight;
float newImageWidth;
float newImageHeight;
int aligmentX = 0;
int aligmentY = 0;
if (pictureRatio <= 1)
{
newImageWidth = imageWidth*(screenHeight/imageHeight);
newImageHeight = screenHeight;
aligmentX = (screenWidth-newImageWidth)/2;
}
else
{
newImageWidth = screenWidth;
newImageHeight = imageHeight*(screenWidth/imageWidth);
aligmentY = (screenHeight - newImageHeight)/2;
}
//end scaling.
CDC *screenDC = GetDC();
CDC mDC;
mDC.CreateCompatibleDC(screenDC);
CBitmap bitMap;
bitMap.CreateCompatibleBitmap(screenDC, screenWidth, screenHeight);
CBitmap *pob = mDC.SelectObject(&bitMap);
mDC.SetStretchBltMode(HALFTONE);
picture.StretchBlt(mDC.m_hDC, aligmentX, aligmentY, newImageWidth, newImageHeight, 0, 0, imageWidth, imageHeight, SRCCOPY);
mDC.SelectObject(pob);
/*.......code to convert bitmap to BYTE* ........*/
/*.......code to send BYTE* over socket........*/
//display the bit map
static_picture.SetBitmap((HBITMAP)bitMap.Detach());
//clean up
ReleaseDC(screenDC);
}
}
所以现在我想更进一步,并尝试使用套接字...是的,我成功地通过套接字发送和接收了简单的 char* 或 CString。
我想做的是:不是在此对话框上显示图片,而是在另一个对话框(服务器)上显示图像。
不知何故,我了解到有 2 个函数听起来很有效:SetBitmapBits()
和 GetBitmapBits()
(老实说,我只是在一些来源上阅读它,不知道它们是否适合我的目标)。
所以,我添加了这段代码,将上面的位图变成了 BYTE 数组 bmpBuffer
:
BITMAP bmpProperties;
bitMap.GetBitmap(&bmpProperties);
int bmpDemension = bmpProperties.bmWidthBytes*bmpProperties.bmHeight;
BYTE* bmpBuffer=(BYTE*)GlobalAlloc(GPTR, bmpDemension);
bitMap.GetBitmapBits(bmpDemension,bmpBuffer);
然后通过套接字发送该数组:
UpdateData(TRUE);
char *socketBuffer = reinterpret_cast<char*>(bmpBuffer);
send(m_ClientSocket, socketBuffer, sizeof(socketBuffer), 0);
//clean up after send
GlobalFree((HGLOBAL)bmpBuffer);
在另一个对话框中。注意:我已经将位图的尺寸硬编码为 160000,只是为了简化问题:
void CMyServer2Dlg::OnReceive(){
char *socketBuffer = new char [1025];
int iLen;
iLen = recv(m_sConnected, socketBuffer, 1025, NULL);
if(iLen==SOCKET_ERROR)
{
AfxMessageBox("Could not Receive");
}
else
{
BYTE* bmpBuffer = reinterpret_cast<BYTE*>(socketBuffer);
//re-construct the bitmap
CBitmap clone;
CDC *screenDC = GetDC();
CDC mDC;
mDC.CreateCompatibleDC(screenDC);
clone.CreateCompatibleBitmap(screenDC, 200, 200);
clone.SetBitmapBits(160000,bmpBuffer);
//Picture control(type bitmap) has variable "static_picture"
static_picture.SetBitmap((HBITMAP)clone.Detach());
UpdateData(FALSE);
ReleaseDC(screenDC);
GlobalFree((HGLOBAL)bmpBuffer);
}
delete socketBuffer;
而且,它根本不起作用...请告诉我我在哪里搞砸了?很抱歉 post.......
我觉得最大的可能是你的接收端没有获取到图片的所有数据。我建议您在发送时将位图的大小放入包中,以便接收方获得正确的大小。
这里有一些示例代码。请注意它们只是为了展示想法,您可能需要进行一些调试以确保它们有效。
第一步:打包位图大小。我想这里的大小小于 64K,所以使用了 int。如果大小可能大于 64k,您可能需要使用 INT64。
int bmpDemension = bmpProperties.bmWidthBytes*bmpProperties.bmHeight;
int bufferSize = bmpDemension + sizeof(int);
BYTE* bmpBuffer=(BYTE*)GlobalAlloc(GPTR, bufferSize );
bitMap.GetBitmapBits(bmpDemension,bmpBuffer + sizeof(int));
memcpy(bmpBuffer, &bmpDemension, sizeof(int)); // put the size into the head of package.
第 2 步:发送出去 请注意,我在这里使用 bufferSize,因为 sizeof(bmpBuffer) returns 指针大小,即 4,而不是 space 大小。
UpdateData(TRUE);
char *socketBuffer = reinterpret_cast<char*>(bmpBuffer);
send(m_ClientSocket, socketBuffer, bufferSize , 0);
//clean up after send
GlobalFree((HGLOBAL)bmpBuffer);
接收端: 首先,你读取位图的大小,然后根据数据的大小进行接收。
void CMyServer2Dlg::OnReceive(){
char socketBuffer[1025];
int iLen;
iLen = recv(m_sConnected, socketBuffer, sizeof(int), NULL); //read the bigmap size
if(iLen==SOCKET_ERROR)
{
AfxMessageBox("Could not Receive");
}
else
{
int dimension = *((int *) socketBuffer);
char * bitmapBuffer = new char[dimension];
int readSize = dimension;
char * pBuffer = bitmapBuffer;
while (readSize > 0)
{
int sizeToRead = readSize > sizeof(socketBuffer) ? sizeof(socketBuffer) : readSize;
iLen = recv(m_sConnected, socketBuffer, sizeToRead , NULL);
memcpy(pBuffer, socketBuffer, iLen);
pBuffer += iLen;
readSize -= iLen;
}
// when the loop done, you shall have all data in bitmapBuffer.
....
// I leave the remaining code to you.
同样,这些代码只是为了演示这个想法。