我住包子山

this->blog.MoveTo("blog.baozishan.in")

VC++编程内幕学习心得(2)--下

接着上回开始

书上的第二个例子

直接贴我略加修改的代码,后面附有注释:

void Cfont2View::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)

{//想说的是OnPrepareDC在每次调用OnDraw之前调用,可在里面进行初始化的一些操作

    CRect clientRect;

   

    GetClientRect(clientRect);

    pDC->SetMapMode(MM_ANISOTROPIC);//横竖比恒定的可变比例映射

    pDC->SetWindowExt(400,450);//窗口范围固定,视口范围如下,因此图像(文字)会根据视口范围改变比例

    pDC->SetViewportExt(clientRect.right, -clientRect.bottom);//原书第二参数为正,我这里用负数,为了和标准MM_TWIPSx,y方向对上,x+,y-;

    CPoint pt = pDC->GetViewportOrg();

    ASSERT(pt.x==0&&pt.y==0);//pDC->SetViewportOrg(0,0);//原书上这样写,应该有点多余,因为初始原点就是0,0;

    CView::OnPrepareDC(pDC, pInfo);

}

void Cfont2View::OnDraw(CDC* pDC)

{//OnDraw函数基本没有变,只是用了一个nPos来一直计算新的字串位置

    CFont fontT1,fontT2,fontT3,fontT4;

    CFont * pOldFont = NULL;

    TEXTMETRIC tm;

    int nPos = 0;

    fontT1.CreateFont(50,0,0,0,400,FALSE,FALSE,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,

        DEFAULT_PITCH|FF_SWISS, TEXT("Arail"));

    pOldFont = pDC->SelectObject(&fontT1);

    pDC->TextOut(0,nPos,TEXT("This is Arial, default width"));

    pDC->GetTextMetrics(&tm);

    nPos-=tm.tmHeight+tm.tmExternalLeading;//找到下一个串的y坐标

    fontT2.CreateFont(50,0,0,0,400,FALSE,FALSE,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,

        DEFAULT_PITCH|FF_MODERN, TEXT("Courier"));

    pDC->SelectObject(&fontT2);

    pDC->TextOut(0,nPos,TEXT("This is Courier, default width"));

    pDC->GetTextMetrics(&tm);

    nPos-=tm.tmHeight+tm.tmExternalLeading;

    fontT3.CreateFont(50,10,0,0,400,FALSE,FALSE,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,

        DEFAULT_PITCH|FF_ROMAN, TEXT("Courier"));

    pDC->SelectObject(&fontT3);

    pDC->TextOut(0,nPos,TEXT("This is generic Roman, variable width"));

    pDC->GetTextMetrics(&tm);

    nPos-=tm.tmHeight+tm.tmExternalLeading;

        fontT4.CreateFont(50,0,0,0,400,FALSE,FALSE,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,

        DEFAULT_PITCH|FF_MODERN, TEXT("LinePrinter"));

    pDC->SelectObject(&fontT4);

    pDC->TextOut(0,nPos,TEXT("This is LinePrinter, default width"));

    pDC->GetTextMetrics(&tm);

    nPos-=tm.tmHeight+tm.tmExternalLeading;

    fontT5.CreateFont(50,0,0,0,400,FALSE,FALSE,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,

        DEFAULT_PITCH|FF_MODERN, TEXT("Consolas"));

    pDC->SelectObject(&fontT5);

    pDC->TextOut(0,nPos,TEXT("Consolas Fonts, recommand by VS2005 official"));

    pDC->SelectObject((CFont*)pOldFont);

 

    Cfont2Doc* pDoc = GetDocument();

    ASSERT_VALID(pDoc);

    if (!pDoc)

        return;

    // TODO: 在此处为本机数据添加绘制代码

}

这个例子大概就是这样,截图放出:

clip_image002

看着这个例子觉得字很丑,于是稍微改了一下背景色跟字体,一下就好看多了,呵呵,代码很简单.需要在OnDraw或者OnPrepareDC中画黑色背景,画字体用CDC::SetBkMode设置透明背景,然后CDC::SetTextColor设置字体颜色为00FF00就可以了,呵呵

clip_image004

接下来就是一个关于深入了解ScrollView的一个例子(第三个例子)

这个例子建立时记得让ViewCScrollView中继承

修改OnInitialUpdate()函数

void CgdiView::OnInitialUpdate()

{

    CScrollView::OnInitialUpdate();

 

    CSize sizeTotal(800,1050);

    CSize sizePage(sizeTotal.cx/2,sizeTotal.cy/2);

    CSize sizeLine(sizeTotal.cx/50,sizeTotal.cy/50);

    SetScrollSizes(MM_LOENGLISH, sizeTotal,sizePage,sizeLine);

}

对于CScrollView类我仍然有一些疑惑,自己未能解决.在设置了ScrollSize以后,仍然可以将view拉长导致超出ScrollSize,这样不会显示滚动条,我试了半天也没有解决问题,真的不知道该怎么办..问题还是留在这里

其他实现如下:

View头文件添加:

private:

    const CSize m_sizeEllipse;

    CPoint m_pointTopLeft;//椭圆外切矩形左上角坐标

    CSize m_sizeOffset;//鼠标初始点中椭圆时与椭圆外切矩形左上角的偏移

BOOL m_bCaptured;//鼠标按下的标志

Cpp文件实现:

void CgdiView::OnDraw(CDC* pDC)

{

    CBrush brushHatch(HS_DIAGCROSS,RGB(255,0,0));//0xFF0000

    CPoint point(0,0);

    pDC->LPtoDP(&point);

    pDC->SetBrushOrg(point);//绘制画刷的原点,如果不这样写,当滚动条滚动时,默认绘制原点会变为当前区域的坐上点坐标

    pDC->SelectObject(&brushHatch);

    pDC->Ellipse(CRect(m_pointTopLeft,m_sizeEllipse));

    TRACE(TEXT("%d %d\n"),m_pointTopLeft.x,m_pointTopLeft.y);

    pDC->SelectStockObject(BLACK_BRUSH);

    pDC->Rectangle(100,-100,200,-200);//这个矩形为了方便查看重绘矩形,当重绘时候矩黑色矩形部分会出现闪烁

    CgdiDoc* pDoc = GetDocument();

    ASSERT_VALID(pDoc);

    if (!pDoc)

        return;

}

void CgdiView::OnLButtonDown(UINT nFlags, CPoint point)

{

    CRect rectEllipse(m_pointTopLeft, m_sizeEllipse);

    CRgn circle;

 

    CClientDC dc(this);

    OnPrepareDC(&dc);

    dc.LPtoDP(rectEllipse);

    circle.CreateEllipticRgnIndirect(rectEllipse);

    if(circle.PtInRegion(point))

    {

        SetCapture();

        m_bCaptured = TRUE;

        CPoint pointTopLeft(m_pointTopLeft);

        dc.LPtoDP(&pointTopLeft);

        m_sizeOffset = point - pointTopLeft;

        SetCursor(LoadCursor(NULL,IDC_CROSS));

    }

    CScrollView::OnLButtonDown(nFlags, point);

}

 

void CgdiView::OnLButtonUp(UINT nFlags, CPoint point)

{

    if(m_bCaptured)

    {

        ReleaseCapture();

        m_bCaptured = FALSE;

    }

    CScrollView::OnLButtonUp(nFlags, point);

}

 

void CgdiView::OnMouseMove(UINT nFlags, CPoint point)

{

    if(m_bCaptured)

    {

        CClientDC dc(this);

        OnPrepareDC(&dc);

        CRect rectOld(m_pointTopLeft, m_sizeEllipse+CSize(1,-1));

        dc.LPtoDP(rectOld);

        InvalidateRect(rectOld, TRUE);

        m_pointTopLeft = point - m_sizeOffset;

        dc.DPtoLP(&m_pointTopLeft);//注意这里有取地址而CRect没有,是因为CRect类定义了一个隐式转换,转换成LPRECT,CPoint没定义

        CRect rectNew(m_pointTopLeft, m_sizeEllipse);

        dc.LPtoDP(rectNew);

        InvalidateRect(rectNew,TRUE);

    }

    CScrollView::OnMouseMove(nFlags, point);

}

 

以下则是位图的基本学习

这一节讲位图

关于创建设备无关位图DIB.

关于DIB最好的资料是MSDN的帮助.

Windows 的两种位图:GDI位图,DIB.

GDI位图由MFC库中的CBitmap类表示.GDI位图对象中有一个相关联的windows数据结构,该数据结构由windowsGDI模块来维护,它是设备相关的.应用程序可以得到GDI位图数据的一份拷贝,但其中每一位的安排完全依赖显示设备.设备依赖的限制就是无法通过调制解调器,磁盘传递这样的位图..

操纵CBitmap跟其他GDI对象是一样的简单..

但是MFC并没有封装DIB,所以需要用SDK的东西来使用DIB.原始使用DIB我觉得是一件很麻烦的事情,你需要了解一些系统调色板之类的知识,有些Directx的书上ms有写.读取一个位图文件需要先知道他的各种信息,之后根据信息为它建立大小合适的结构存放他的位图信息,根据这些为位图设定位图的调色版,在绘制位图时有可能需要把位图调色板让系统使用,之后再恢复成原始的调色板,很多很复杂的操作..但是,这本书的作者提供给我们了一个他写的CDib,降低了开发的难度,节省了时间,网上ms有很多CDib的类:

我将附上去它的整个代码部分,如果能研究好了这部分内容应该会有很大提高,目前我还没研究太好..只是大概有那么点概念

头文件直接贴在这儿

// cdib.h declaration for Inside Visual C++ CDib class

 

#ifndef _INSIDE_VISUAL_CPP_CDIB

#define _INSIDE_VISUAL_CPP_CDIB

 

class CDib : public CObject

{

    enum Alloc {noAlloc, crtAlloc, heapAlloc};

    DECLARE_SERIAL(CDib)

public:

    LPVOID m_lpvColorTable;

    HBITMAP m_hBitmap;

    LPBYTE m_lpImage;  // starting address of DIB bits

    LPBITMAPINFOHEADER m_lpBMIH; //  buffer containing the BITMAPINFOHEADER

private:

    HGLOBAL m_hGlobal; // For external windows we need to free;

                       //  could be allocated by this class or allocated externally

    Alloc m_nBmihAlloc;

    Alloc m_nImageAlloc;

    DWORD m_dwSizeImage; // of bits -- not BITMAPINFOHEADER or BITMAPFILEHEADER

    int m_nColorTableEntries;

   

    HANDLE m_hFile;

    HANDLE m_hMap;

    LPVOID m_lpvFile;

    HPALETTE m_hPalette;

public:

    CDib();

    CDib(CSize size, int nBitCount);    // builds BITMAPINFOHEADER

    ~CDib();

    int GetSizeImage() {return m_dwSizeImage;}

    int GetSizeHeader()

        {return sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * m_nColorTableEntries;}

    CSize GetDimensions();

    BOOL AttachMapFile(const char* strPathname, BOOL bShare = FALSE);

    BOOL CopyToMapFile(const char* strPathname);

    BOOL AttachMemory(LPVOID lpvMem, BOOL bMustDelete = FALSE, HGLOBAL hGlobal = NULL);

    BOOL Draw(CDC* pDC, CPoint origin, CSize size);  // until we implemnt CreateDibSection

    HBITMAP CreateSection(CDC* pDC = NULL);

    UINT UsePalette(CDC* pDC, BOOL bBackground = FALSE);

    BOOL MakePalette();

    BOOL SetSystemPalette(CDC* pDC);

    BOOL Compress(CDC* pDC, BOOL bCompress = TRUE); // FALSE means decompress

    HBITMAP CreateBitmap(CDC* pDC);

    BOOL Read(CFile* pFile);

    BOOL ReadSection(CFile* pFile, CDC* pDC = NULL);

    BOOL Write(CFile* pFile);

    void Serialize(CArchive& ar);

    void Empty();

   

    BOOL CDib::DrawDib(CDC* pDC, CPoint origin, CSize size);

private:

    void DetachMapFile();

    void ComputePaletteSize(int nBitCount);

    void ComputeMetrics();

};

#endif // _INSIDE_VISUAL_CPP_CDIB

两个构造函数:

CDib();

CDib(CSize size, int nBitCount);    // builds BITMAPINFOHEADER

第一个没有参数的构造函数建立一个空的DIB对象,用来从文件或内存载入DIB.

第二个带有高宽,颜色位数的构造函数,可以用它的CreateSection,得到DIBSection.

其他Public的成员函数:

BOOL AttachMapFile(const char* strPathname, BOOL bShare = FALSE);

BOOL AttachMemory(LPVOID lpvMem, BOOL bMustDelete = FALSE, HGLOBAL hGlobal = NULL);

从内存映射文件读出位图/从内存地址,有可能需要用全局句柄(HGLOBAL)读出位图

BOOL Compress(CDC* pDC, BOOL bCompress = TRUE); // FALSE means decompress

压缩,解压缩位图

BOOL CopyToMapFile(const char* strPathname);

建立一个映射文件把当前DIB内容复制过去,当文件对象关闭时(CDib对象析构)真正写入文件..

...省略很多函数说明,有问题可以查书,呵呵,下面介绍应用CDib的方法

 

注意要链接"VFW32.lib,因为在CDib中有(#include <vfw.h>)

使用CDib的方法:

调用Draw之前需要做的

m_pDib->UsePalette(pDC); // could be in palette msg handler

CSize sizeDib = m_pDib->GetDimensions();

pDC->StretchBlt(0, 0, sizeDib.cx, sizeDib.cy, &m_dcMem,

                0, 0, sizeToDraw.cx, sizeToDraw.cy, SRCCOPY);

DIB的例子在下面:

建立工程使用的是CScrollView视图,

    CDib m_dibFile;

    CDib m_dibResource;

两个成员函数,分别从资源文件载入位图和从(映射)文件载入位图

void CEx06dView::OnInitialUpdate()//初始化滚动条跟映射模式,载入资源视图

{

    CScrollView::OnInitialUpdate();

    CSize sizeTotal(30000, 40000); // 30-by-40 cm

    CSize sizeLine = CSize(sizeTotal.cx / 100, sizeTotal.cy / 100);

    SetScrollSizes(MM_HIMETRIC, sizeTotal, sizeTotal, sizeLine);

 

    LPVOID lpvResource = (LPVOID) ::LoadResource(NULL,

        ::FindResource(NULL, MAKEINTRESOURCE(IDB_GOHAN),

                       RT_BITMAP));

    m_dibResource.AttachMemory(lpvResource); // no need for

                                             //  ::LockResource

    CClientDC dc(this);

    TRACE("bits per pixel = %d\n", dc.GetDeviceCaps(BITSPIXEL));

 

}

OnDraw函数,使用了两个dib类型,第一个用SDK,StretchDIBits绘制图像,第二个用CDib类型绘制图像

void CEx06dView::OnDraw(CDC* pDC)

{

    CEx06dDoc* pDoc = GetDocument();

    BeginWaitCursor();

    m_dibResource.UsePalette(pDC); // should be in palette

    m_dibFile.UsePalette(pDC);     //  message handlers, not here

    pDC->TextOut(0, 0,

        "Press the left mouse button here to load a file.");

    CSize sizeResourceDib = m_dibResource.GetDimensions();

    sizeResourceDib.cx *= 30;

    sizeResourceDib.cy *= -30;

    //m_dibResource.Draw(pDC, CPoint(0, -800), sizeResourceDib);

    pDC->SetStretchBltMode(COLORONCOLOR);

    StretchDIBits(pDC->GetSafeHdc(),0,-800,sizeResourceDib.cx,sizeResourceDib.cy,0,0,sizeResourceDib.cx/30,sizeResourceDib.cy/-30,

        m_dibResource.m_lpImage,(LPBITMAPINFO)m_dibResource.m_lpBMIH, DIB_RGB_COLORS, SRCCOPY);

    CSize sizeFileDib = m_dibFile.GetDimensions();

    sizeFileDib.cx *= 30;

    sizeFileDib.cy *= -30;

    m_dibFile.Draw(pDC, CPoint(1800, -800), sizeFileDib);

    EndWaitCursor();

 

}

鼠标左键点击事件,你可以使用MEMORY_MAPPED_FILES宏定义,这时候用的是内存映射文件,否则使用的就是文件,对应的CDib方法分别为AttachMapFile, Read..

//#define MEMORY_MAPPED_FILES

void CEx06dView::OnLButtonDown(UINT nFlags, CPoint point)

{

    CFileDialog dlg(TRUE, "bmp", "*.bmp");

    if (dlg.DoModal() != IDOK) {

        return;

    }

#ifdef MEMORY_MAPPED_FILES

    if (m_dibFile.AttachMapFile(dlg.GetPathName(),

            TRUE) == TRUE) { // share

        Invalidate();

    }

 #else

    CFile file;

    file.Open(dlg.GetPathName(), CFile::modeRead);

    if (m_dibFile.Read(&file) == TRUE) {

        Invalidate();

    }

#endif // MEMORY_MAPPED_FILES

    CClientDC dc(this);

    m_dibFile.SetSystemPalette(&dc);

}

clip_image006

对于调色板的使用我并没有很好的掌握,之后还会看看,如果想差不多会再补上,明天加上最后一小节.

posted on 2007-12-08 18:04 Gohan 阅读(1994) 评论(2)  编辑 收藏 引用 所属分类: MFC/SDK

Feedback

# re: VC++编程内幕学习心得(2)--下 2007-12-08 18:15 Gohan

http://www.cppblog.com/Files/gohan/CDIB.rar
CDIB类下载,包括cpp文件跟头文件  回复  更多评论   

# re: VC++编程内幕学习心得(2)--下 2007-12-18 21:35 秦歌

顶一下  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理