⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 base7.htm

📁 C++Builder教学大全
💻 HTM
📖 第 1 页 / 共 3 页
字号:
函数init_image从文件中装入图像。它通过文件扩展名来判断图像类型。<br>      
该函数接受下列扩展名:<br>      
.BMP 即 Windows DIB 文件。<br>       
.PCX 即 PC PaintBrush 文件。<br>       
.TGA 即 24 位,Truevision Targa 文件。<br>       
<br>      
上面介绍了如何生成背景图像,子图形的基本概念。这一节让我们看一下怎样创建一个或两个子图形,并把它们添加到场景中去。<br>      
为何需要通知视图在场景中装入了一个新的子图形有两个原因:首先,子图形只有被画出来才能成为可见的,而第二,子图形中的颜色必须被映射到由屏幕视图缓冲区使用的调色板中。<br>      
注意,即使背景和子图形使用的是同一个调色板,但由于考虑到系统颜色和调色板项的逻辑到物理映射之间的作用方式,仍然需要将子图形映射到屏幕缓冲区调色板上。屏幕缓冲区中的调色板是由背景DIB中的颜色表而创建的。当调色板成为一种等同调色板时(出于最好性能的考虑),可以对颜色进行重新排序,因此背景DIB和所有子图形中的象素必须被改变以具有在新的(等同)调色板中的正确索引。如果你没有将子图形的颜色映射到屏幕缓冲区中,那么显示出来的子图形颜色将都是错误的。<br>      
首先,让我们看一看简单的CAnimView: :NewSprite函数:<br>       
void CAnimView::NewSprite(CSprite* pSprite)<br>       
{ kk1}<br>       
// Map the colors in the sprite DIB to the<br>       
// palette in the off-screen buffered view.<br>       
if (m_pPal) {kk1}<br>       
pSprite-&gt;MapColorsToPalette(m_pPal);<br>      
}<br>      
<br>      
// Render the scene with the new sprite.<br>       
Render();<br>      
<br>      
// Draw the result.<br>       
Draw();<br>      
}<br>      
通过调用MapColorsToPalette函数,DIB的颜色被映射到调色板上。调用Render函数在屏幕缓冲区构画子图形图象,然后Draw函数将缓冲区拷贝到屏幕上,使子图形在场景中可见。Map        
Colors To Palette函数和我们为CDIB类开发的函数几乎完全一样:<br>       
BOOL CSprite::MapColorsToPalette(CPalette* pPal)<br>       
{kk1}<br>      
BOOL bResult = CDIB::MapColorsToPalette(pPal);<br>       
// Get the transparency info again.<br>       
Initialize();<br>      
return bResult;<br>       
}<br>      
事实上,你可以看到CDIB: :Map Colors To Palette被调用来进行实际的映射,而CSprite类所特有的唯一工作是再次获取透明信息,这是因为我们使用的是调色板索引来定义子图形图象中的哪种颜色是透明的,因而需要在调用Map        
Colors To Palette时被重新映射。<br>       
需要对Render函数进行一点小的改进以处理在构画背景图象之外还得构画子图形。<br>      
void CAnimView::Render(CRect* pClipRect)<br>       
{kk1}<br>      
CAnimDoc* pDoc = GetDocument();<br>       
CRect rcDraw;<br>       
<br>      
// Get the background DIB and render it.<br>       
CDIB* pDIB = pDoc-&gt;GetBackground();<br>       
if (pDIB) {kk1}<br>       
pDIB-&gt;GetRect(&amp;rcDraw);<br>      
// If a clipping rectangle was supplied, use it.<br>       
if (pClipRect) {kk1}<br>       
rcDraw.IntersectRect(pClipRect, &amp;rcDraw);<br>       
}<br>      
<br>      
// Draw the image of the DIB to the off-screen buffer.<br>       
ASSERT(m_pDIB);<br>      
pDIB-&gt;CopyBits(m_pDIB,&nbsp;<br>      
rcDraw.left,<br>      
rcDraw.top,&nbsp;<br>      
rcDraw.right - rcDraw.left,<br>       
rcDraw.bottom - rcDraw.top,<br>       
rcDraw.left,&nbsp;<br>      
rcDraw.top);<br>      
}<br>      
<br>      
// Get the sprite and render it.<br>       
CSprite* pSprite = pDoc-&gt;GetSprite();<br>       
if (pSprite) {kk1}<br>       
pSprite-&gt;Render(m_pDIB, &amp;rcDraw);<br>       
}<br>      
}<br>      
背景图象被首先画到缓冲区。如果存在一个子图形,那么它也被画到屏幕缓冲区,位于背景之上。这就是本书中所说的“面向对象欺骗”。我们并没有书写大量的代码来将子图形的图象画到屏幕缓冲区,而只是让子图形自己来完成这一工作。这样作意义重大,因为这种作法使子图形的特有代码集中在子图形对象中,从而使我们可以将注意力放在功能上而不必纠缠在实现细节上。<br>      
当然,还得我们来书写子图形的构图代码,因此让我们看一看下面的代码:<br>      
void CSprite::Render(CDIB* pDIB, CRect* pClipRect)<br>       
{kk1}<br>      
ASSERT(pDIB);<br>      
<br>      
// Get the sprite rectangle.<br>       
CRect rcDraw;<br>       
GetRect(&amp;rcDraw);<br>      
<br>      
// If a clipping rectangle was supplied, see if the sprite<br>       
// is visible inside the rectangle.<br>       
if (pClipRect) {kk1}<br>       
if (!rcDraw.IntersectRect(pClipRect, &amp;rcDraw)) {kk1}<br>       
return; // Not visible<br>       
}<br>      
// Copy the image of the sprite.<br>       
CopyBits(pDIB, // Dest DIB<br>       
rcDraw.left, // Dest x<br>       
rcDraw.top, // Dest y<br>       
rcDraw.right - rcDraw.left,// Width<br>       
rcDraw.bottom - rcDraw.top,// Height<br>       
rcDraw.left - m_x, // Source x<br>       
rcDraw.top - m_y, // Source y<br>       
PALETTEINDEX(m_bTransIndex));&nbsp;<br>      
// Transparent color index<br>       
}<br>      
当然我们需要检验子图形是否在裁剪板矩形中是可见的(如果提供了裁剪板矩形的话)。一旦计算出实际的绘图矩形后,CDIB::CopyBits函数被用来将子图形图像的非透明部分拷贝到屏幕缓冲区中。CDIB::CopyBits的透明特征是一种新的特性,请读者在动画设计时注意。<br>      
<br>      
为了实现重画区域,需要将一个区域列表保存在某个位置,以及一种往列表中添加列表项的方法,而当要执行一次构图和画图动作时,需要一种遍历列表的方法。因为构图和画图主要涉及到COSBView类中的屏幕缓冲区,因此作者决定在COSBView中增加重画列表及其管理函数。<br>      
首先来看一看应用程序的其余部分是如何使用重画列表的,然后再进一步讨论重画列表的函数是如何实现的。先看一看下面的这个代码,此代码实现了对ANIMVIEW.        
CPP中一个子图形的鼠标拖动:<br>      
void CAnimView::OnMouseMove(UINT nFlags, CPoint point)<br>       
{kk1}<br>      
if (m_bMouseCaptured) {kk1}<br>       
ASSERT(m_pCapturedSprite);<br>      
CRect rcOld, rcNew;<br>       
m_pCapturedSprite-&gt;GetRect(&amp;rcOld);<br>      
m_pCapturedSprite-&gt;SetPosition(point.x - m_ptOffset.x,&nbsp;<br>       
point.y - m_ptOffset.y);<br>       
m_pCapturedSprite-&gt;GetRect(&amp;rcNew);<br>      
// Add the changed regions to the view's dirty list.<br>       
AddDirtyRegion(&amp;rcOld);<br>      
AddDirtyRegion(&amp;rcNew);<br>      
// Render and draw the changes.<br>       
RenderAndDrawDirtyList();<br>      
}<br>      
}<br>      
子图形的位置被保存在一个CRect对象中。移动子图形,并获得其新的位置。通过调用AddDirtyRegion,将新旧矩形添加到重画列表中。一旦这些区域被添加到列表中,将产生对RenderAndDrawDirtyList的一个调用从而画出改变后的区域。AddDirtyRegion和RenderAndDraWDirtyList都是COSBView中的新函数。<br>      
COSBView类具有一个新的对象来保存重画列表和几个新的成员函数。下面是COSBVIEW.        
H的新添加的部分:<br>      
class COSBView : public CScrollView<br>       
{kk1}<br>      
<br>      
public:<br>      
virtual void Render(CRect* pClipRect = NULL) {kk1}return;}<br>       
void AddDirtyRegion(CRect* pRect);<br>       
void RenderAndDrawDirtyList();<br>       
<br>      
private:<br>      
CObList m_DirtyList; // Dirty regions<br>       
<br>      
void EmptyDirtyList();<br>       
};<br>      
<br>      
#ifndef _DEBUG // Debug version in osbview.cpp<br>       
inline CDocument* COSBView::GetDocument()<br>       
{kk1} return (CDocument*) m_pDocument; }<br>       
注意重画列表是作为由Microsoft基类库(MFC)所提供的一个CObList对象实现的。CObList类保留了一个对象指针的列表,而其中每一个对象又都是从CObject类导出的。此表在这用于保存指向CRect对象(重画区域在这里的实现)的指针,而通过创建一个由CObList导出而只处理CRect对象的类,从而使实现更多了一些类安全性。采用这种方法要求进行类型强制,因此对新手来说也更多了一些危险性。现在来看一看AddDirtyRegion函数,这样可以了解列表的使用方法:<br>      
void COSBView::AddDirtyRegion(CRect* prcNew)<br>       
{kk1}<br>      
// Get the rectangle currently at the top of the list.<br>       
POSITION pos = m_DirtyList.GetHeadPosition();<br>       
if (pos) {kk1}<br>       
CRect* prcTop = CRect*)m_DirtyList.GetNext(pos);<br>       
CRect rcTest;<br>       
// If the new one intersects the top one merge them.<br>       
if (rcTest.IntersectRect(prcTop, prcNew)) {kk1}<br>       
prcTop-&gt;UnionRect(prcTop, prcNew);<br>       
return;<br>      
}<br>      
}<br>      
// List is empty, or there was no intersection.<br>       
CRect* prc = new CRect;<br>       
*prc = *prcNew; // Copy the data.<br>       
// Add a new rectangle to the list. . m_DirtyList.AddHead((CObject*)prc);<br>       
}<br>      
记住在这里使用的唯一优化是如果一个新添加的矩形和重画列表中位于其上面的那个矩形之间存在重叠,则进行合并。代码首先获得位于列表顶部的矩形——注意CRect*强制,如果列表是空的,那么新的矩形被简单地添加到列表的顶部,如果在列表的顶部<br>      
已经存在有一个矩形,则检测该矩形是否和新矩形之间有相交。如果有,则简单地进行扩大以包括新的矩形。如果没有发现有交叉,则将新矩形添加到列表的顶部。<br>      
现在,让我们再来看一看<br>      
RenderAndDrawDirtyList函数:<br>      
void COSBView::RenderAndDrawDirtyList()<br>       
{kk1}<br>      
POSITION pos = m_DirtyList.GetHeadPosition();<br>       
// Render all the dirty regions.<br>       
while (pos) {kk1}<br>       
// Get the next region.<br>       
CRect* pRect = (CRect*)m_DirtyList.GetNext(pos);<br>       
// Render it.<br>       
Render(pRect);<br>      
}<br>      
// Draw all the dirty regions to the screen.<br>       
while (!m_DirtyList.IsEmpty()) {kk1}<br>       
// Get the next region.<br>       
CRect* pRect = (CRect*)m_DirtyList.RemoveHead();<br>       

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -