接上一章

一、在文档类中添加两个CDib成员变量:m_dib, m_newDib,一个int 成员变量m_stateDoc和一个CString成员变量m_fileName

int m_stateDoc; // 用于判断是否打开了文件
CDib m_newDib; // CDib类对象保存图像,用于获取一些必要的信息
CDib m_dib; // CDib类对象保存原图数据,在初始化图像数据时有用
CString m_fileName; // 保存图像名称路径

二、在文档类中添加打开文件菜单的消息处理:

 

 

void CScanPictureDoc::OnFileOpen()
{
// TODO: Add your command handler code here
// 创建打开文件对话框
CFileDialog dlg(TRUE, _T("BMP"), _T("*.BMP"), OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, _T("位图文件(*.BMP)|*.BMP|"));
if(IDOK == dlg.DoModal())
{// 文件打开成功后,获取文件路径,并把图像载入m_dib和m_newDib两个类中,m_stateDoc = 1表明文件已打开
m_fileName.Format("%s", dlg.GetPathName());
m_dib.LoadFile(m_fileName);
m_newDib.LoadFile(m_fileName);
m_stateDoc = 1;
}
}

三、在View窗口类中添加两个CDib *成员变量:m_pDib, m_pNewDib和一个int成员变量m_state,并且添加初始化图像数据的成员函数InitializePictureData

 

void CScanPictureView::InitializePictureData()
{
// 获取保存的文档
CScanPictureDoc * pDoc = GetDocument();
ASSERT_VALID(pDoc);
m_state = 1;  // 表明已经从文档中获取数据
// 如果文档中已经打开文件,就获取图像数据,否则设为空
if(pDoc->m_stateDoc)
{
m_pNewDib = &pDoc->m_newDib;
m_pDib = &pDoc->m_dib;
long int size = m_pDib->GetHeight() * m_pDib->GetWidth();
memcpy(m_pNewDib->m_pData, m_pDib->m_pData, size);
}
else
{
m_pNewDib = NULL;
m_pDib = NULL;
}
}

四、在View类中添加CPalette成员变量m_palette和创建调色板成员函数CreateDibPalette

 

CPalette * CScanPictureView::CreateDibPalette(CDib *pDib)
{
// 调色板结构,不可直接用LOGPALETTE palette = {0x300, 256};
// 因为LOGPALETTE的结构是
//  typedef struct tagLOGPALETTE { // lgpl
//      WORD         palVersion;
//      WORD         palNumEntries;
//      PALETTEENTRY palPalEntry[1];
//  } LOGPALETTE;
struct
{
WORD Version;
WORD NumberOfEntries;
PALETTEENTRY aEntries[256];
}palette = {0x300, 256};
// 获取图像颜色表和颜色数量
LPRGBQUAD pRGBTable = pDib->GetRGB();
UINT numberOfColors = pDib->GetNumOfClrs();
// 复制颜色表到调色板
for(UINT x = 0; x != numberOfColors; x++)
{
palette.aEntries[x].peRed = pRGBTable[x].rgbRed;
palette.aEntries[x].peGreen = pRGBTable[x].rgbGreen;
palette.aEntries[x].peBlue = pRGBTable[x].rgbBlue;
palette.aEntries[x].peFlags = 0;
}
// 如果已经创建过调色板,先删除掉,重新创建
if(m_palette.m_hObject != NULL)
m_palette.DeleteObject();
m_palette.CreatePalette((LPLOGPALETTE) &palette);
return &m_palette; // 返回调色板
}

五、在View类头文件中添加宏:

 

#define DIRECT_NONE1000// 直接显示图像
#define DIRECT_DOWN1001// 向下方向扫描显示图像
#define DIRECT_UP1002// 向上方向扫描显示图像
#define DIRECT_LEFT1003// 向左方向扫描显示图像
#define DIRECT_RIGHT1004// 向右方向扫描显示图像

再添加扫描显示成员函数ScanPicture

 

void CScanPictureView::ScanPicture(int whichDirect)
{
// 获取并初始化图像显示区域
CDC *pDC = GetDC();
CRect rect;
InitPictureShowRect(pDC, rect);
InitializePictureData(); // 初始化图像数据
// 获取程序保存的文档对象
CScanPictureDoc * pDoc = GetDocument();
ASSERT_VALID(pDoc);
// 判断是否打开文件并成功初始化图像数据
if(pDoc->m_stateDoc && m_state == 1)
{
BYTE * pDibData = m_pNewDib->GetData(); // 获取图像数据
LPBITMAPINFO pDibInfo = m_pNewDib->GetInfo(); // 获取图像信息
int dibHeight = m_pNewDib->GetHeight(); // 获取图像高度
int dibWidth = m_pNewDib->GetWidth(); // 获取图像宽度
// 判断是否有图像颜色表信息
if(m_pNewDib->GetRGB())
{
// 创建调色板,并选入设备
CPalette * hPalette = CreateDibPalette(m_pNewDib);
CPalette * hOldPalette = pDC->SelectPalette(hPalette, true);
pDC->RealizePalette();
switch(whichDirect)
{
case DIRECT_DOWN:
{// 图像从上到下扫描显示的代码块
for(int i = 0; i != dibHeight; i++)
{
::StretchDIBits(pDC->GetSafeHdc(), rect.left + (rect.Width() - dibWidth) / 2,
rect.top + (rect.Height() - dibHeight) / 2 + i,
dibWidth, 1, 0, dibHeight - i, dibWidth, 1, pDibData, pDibInfo, DIB_RGB_COLORS, SRCCOPY);
Sleep(5);
}
}
break;
case DIRECT_UP:
{// 图像从上到下扫描显示的代码块
for(int i = 0; i != dibHeight; i++)
{
::StretchDIBits(pDC->GetSafeHdc(), rect.left + (rect.Width() - dibWidth) / 2,
rect.top + (rect.Height() + dibHeight) / 2 - i,
dibWidth, 1, 0, i, dibWidth, 1, pDibData, pDibInfo, DIB_RGB_COLORS, SRCCOPY);
Sleep(5);
}
}
break;
case DIRECT_LEFT:
{// 图像从左到右扫描显示的代码块
for(int i = 0; i != dibWidth; i++)
{
::StretchDIBits(pDC->GetSafeHdc(), rect.right - (rect.Width() - dibWidth) / 2 - i,
rect.top + (rect.Height() - dibHeight) / 2, 1, dibHeight, dibWidth - i, 0, 1, dibHeight,
pDibData, pDibInfo, DIB_RGB_COLORS, SRCCOPY);
Sleep(5);
}
}
break;
case DIRECT_RIGHT:
{// 图像从右到左扫描显示的代码块
for(int i = 0; i != dibWidth; i++)
{
::StretchDIBits(pDC->GetSafeHdc(), rect.left + (rect.Width() - dibWidth) / 2 + i,
rect.top + (rect.Height() - dibHeight) / 2, 1, dibHeight, i, 0, 1, dibHeight,
pDibData, pDibInfo, DIB_RGB_COLORS, SRCCOPY);
Sleep(5);
}
}
break;
case DIRECT_NONE:
{// 直接将图像拷贝到设备上显示
::StretchDIBits(pDC->GetSafeHdc(), rect.left + (rect.Width() - dibWidth) / 2, rect.top + (rect.Height() - dibHeight) / 2,
dibWidth, dibHeight, 0, 0, dibWidth, dibHeight, pDibData, pDibInfo, DIB_RGB_COLORS, SRCCOPY);
}
break;
}
// 选回之前的调色板,并删除新建的调色板
pDC->SelectPalette(hOldPalette, true);
::DeleteObject(hPalette);
}
}
}

 

                图像扫描显示的算法分析:图像扫描显示的效果是先把位图文件拷贝到内存的位图对象中,然后按某个方向一行一行的复制到相应设备上,在复制的同时适当的间隔显示时间,就能看到逐行扫描的效果了。

        如向下扫描伪代码:

第一步:×××变量赋值 1,执行第二步;

第二步:判断是否小于图像高度dibHeight,是就执行第三步,否则执行第七步;

第三步:把第dibHeight - i行的图像复制到设备的第i行上(注意:图像文件保存的图像数据是从下到上方向保存的),执行第四步;

第四步:变量i1,执行第五步;

第五步:暂停程序5毫(Sleep(5)),执行第六步;

第六步:返回执行第二步;

第七步:算法完成。

六、最后在OnDra和按钮消息处理函数中添加ScanPicture,如下:

void CScanPictureView::OnDraw(CDC* pDC)
{
CScanPictureDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
// 获取窗口客户区矩形
CRect rcViewClient;
GetClientRect(rcViewClient);
//绘制分割线和按钮
DrawSeparator(rcViewClient);
DrawButtons(rcViewClient);
ScanPicture(DIRECT_NONE);
}

至此,我们的扫描小程序就完成了,效果如下:

MFC图像处理-图像扫描显示之扫描显示_第1张图片