您的位置:首页 > 其它

MFC图像处理-图像扫描显示之扫描显示

2013-07-02 20:32 274 查看
接上一章
一、在文档类中添加两个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);
}
}
}


​ ​图像扫描显示的算法分析:图像扫描显示的效果是先把位图文件拷贝到内存的位图对象中,然后按某个方向一行一行的复制到相应设备上,在复制的同时适当的间隔显示时间,就能看到逐行扫描的效果了。
如向下扫描伪代码:
第一步:整形变量i 赋值 1,执行第二步;
第二步:判断是否小于图像高度dibHeight,是就执行第三步,否则执行第七步;
第三步:把第dibHeight - i行的图像复制到设备的第i行上(注意:图像文件保存的图像数据是从下到上方向保存的),执行第四步;
第四步:变量i加1,执行第五步;
第五步:暂停程序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);

}


void CScanPictureView::OnScanDownBtnClicked()

{

ScanPicture(DIRECT_DOWN);

}

void CScanPictureView::OnScanUpBtnClicked()

{

ScanPicture(DIRECT_UP);

}

void CScanPictureView::OnScanLeftBtnClicked()

{

ScanPicture(DIRECT_LEFT)

}

void CScanPictureView::OnScanRightBtnClicked()

{

ScanPicture(DIRECT_RIGHT);

}


至此,我们的扫描小程序就完成了,效果如下:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: