epareDC(&dc);
CRect rectInvalid = pStroke->GetBoundingRect();
dc.LPtoDP(&rectInvalid);
InvalidateRect(&rectInvalid);
return;
}
}
// We can't interpret the hint, so assume that anything might
// have been updated.
Invalidate(TRUE);
return;
}
这里,传给pHint指针的内容是指向需要绘制的笔画对象的指针。采用强制类型转换将它转换为笔划指针,然后取得包围该笔划的最小矩形。OnPrepareDC用于调整视图坐标原点。由于InvalidateRect需要设备坐标,因此调用LPToDP(&rectInvalid)将逻辑坐标转换为设备坐标。最后,调用InvalidateRect是窗口部分区域“无效”,也就是视图在收到WM_PAINT消息后需要重绘这一区域。
InvalidateRect函数原型为:
void InvalidateRect( LPCRECT lpRect, BOOL bErase = TRUE );
第一个参数是指向要重绘的矩形的指针,第二个参数告诉视图是否要删除区域内的背景。
这样,当需要重画某一笔划时,只需要重画包围笔划的最小矩形部分就可以了,其他部分就不再重绘。这也是为什么在笔划对象中提供最小矩形信息的原因。
如果pHint为空,则表明是一般的重绘,此时需要重绘整个客户区。
现在,在OnDraw中,根据无效矩形绘制图形,而不是重绘全部笔划,见清单8.6。
清单8.6 根据无效矩形绘制图形的OnDraw成员函数
void CDrawView::OnDraw(CDC* pDC)
{
CDrawDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// Get the invalidated rectangle of the view, or in the case
// of printing, the clipping region of the printer dc.
CRect rectClip;
CRect rectStroke;
pDC->GetClipBox(&rectClip);
pDC->LPtoDP(&rectClip);
rectClip.InflateRect(1, 1); // avoid rounding to nothing
// Note: CScrollView::OnPaint() will have already adjusted the
// viewport origin before calling OnDraw(), to reflect the
// currently scrolled position.
// The view delegates the drawing of individual strokes to
// CStroke::DrawStroke().
CTypedPtrList& strokeList = pDoc->m_strokeList;
POSITION pos = strokeList.GetHeadPosition();
while (pos != NULL)
{
CStroke* pStroke = strokeList.GetNext(pos);
rectStroke = pStroke->GetBoundingRect();
pDC->LPtoDP(&rectStroke);
rectStroke.InflateRect(1, 1); // avoid rounding to nothing
if (!rectStroke.IntersectRect(&rectStroke, &rectClip))
continue;
pStroke->DrawStroke(pDC);
}
// TODO: add draw code for native data here
}
OnDraw首先调用GetClipBox取得当前被剪裁区域(无效矩形区域),它把矩形复制导GetClipBox的参数rectClip中。然后将rectClip的坐标由逻辑坐标转换为设备坐标。为了防止该矩形太小而无法包围其他内容,上下各放大一个单位。然后OnDraw遍历笔划链表中的所有笔划,获取它们的最小矩形,用IntersectRect看它是否与无效矩形相交。如果相交,说明笔划的部分或全部落在无效矩形中,此时调用笔划的DrawStroke方法画出该笔划。
图8-6 根据包围笔划 的矩形是否与无效
矩形相交 ,判断笔划是否落入无效矩形中
为了获得笔划的最小包围矩形,需要在结束笔划时计算出包围笔划的最小矩形。因此为笔划提供两个方法:一个是FinishStroke(),用于在笔划结束时计算最小矩形,见清单8.7。
清单8.7 CStroke::FinishStroke()成员函数
void CStroke::FinishStroke()
{
// Calculate the bounding rectangle. It's needed for smart
// repainting.
if (m_pointArray.GetSize()==0)
{
m_rectBounding.SetRectEmpty();
return;
}
CPoint pt = m_pointArray[0];
m_rectBounding = CRect(pt.x, pt.y, pt.x, pt.y);
for (int i=1; i < m_pointArray.GetSize(); i++)
{
// If the point lies outside of the accumulated bounding
// rectangle, then inflate the bounding rect to include it.
pt = m_pointArray[i];
m_rectBounding.left = min(m_rectBounding.left, pt.x);
m_rectBounding.right = max(m_rectBounding.right, pt.x);
m_rectBounding.top = max(m_rectBounding.top, pt.y);
m_rectBounding.bottom = min(m_rectBounding.bottom, pt.y);
}
// Add the pen width to the bounding rectangle. This is necessary
// to account for the width of the stroke when invalidating
// the screen.
m_rectBounding.InflateRect(CSize(m_nPenWidth, -(int)m_nPenWidth));
return;
}
另一个是DrawStroke(),用于绘制笔划:
BOOL CStroke::DrawStroke(CDC* pDC)
{
CPen penStroke;
if (!penStroke.CreatePen(PS_SOLID, m_nPenWidth, RGB(0,0,0)))
return FALSE;
CPen* pOldPen = pDC->SelectObject(&penStroke);
pDC->MoveTo(m