[GuanRuiPaint]How to implement Rect Trackers in bitmap editor?

(一) 关于如何结合RectTracker类实现一个绘图区的大小Resizable的问题

想做到和MSPAINT一摸一样的功能的话,如何对客户区中的DIB块(画布)进行处理就是关键了。
而且,想编程实现多层的设计,也需要深入理解这一块。
想在客户区中的DIB块中添加Hit Button, 也就是小点,拖动该点可以重新设置画布的大小。下图:




我现在的想法是,使用CRectTracker来实现一个带HitButton的矩形框,然后,将该框与画布重叠,这样的话,显示给用户的画布边缘区域就存在HitButton。当然,只是设想,具体实现情况,过几天才能知道。
自己MARK这个MILESTONE,如果这块完成,接下来的操作就较为简单了。

下一步是实现对区块的拖动等操作,基本上,一个简单的,基于GDI的画笔程序就完成了。

这个程序虽然不复杂,但是,基本上GDI的一些操作都包含有,对于GDI初学者,还是很有帮助的。
写这种程序来练练手,非常实用.


(二)尝试成功
哈哈,it works, 尝试成功,使用CTectTracker 可以完全实现这个功能
Tracker can also be applied to any normal application. In a graphic editor, tracker can be used to select a rectangular region, move and drop it anywhere within the image. It can also be used to indicate the selected rectangular area when we implement cut, copy and paste commands.

In the View class, OnDraw() function add such code below: 

if (! pDoc->m_pDib->IsEmpty())
        m_pDib->Display(pDC, 0, 0);

    m_trackerSel.m_rect.left = 0;
    m_trackerSel.m_rect.top = 0;
    m_trackerSel.m_rect.right  = pDoc->m_pDib->GetWidth();
    m_trackerSel.m_rect.bottom = pDoc->m_pDib->GetHeight();

if(!m_trackerSel.m_rect.IsRectEmpty())
{
    m_trackerSel.m_rect.OffsetRect(-5);
    m_trackerSel.Draw(pDC);
    m_trackerSel.m_rect.OffsetRect(5);
}

it will set the Tracker's border overlap with the DIB area. the final effect is as shown as below.
of course,我们可以先绘制矩形框,然后控制大小,这在后面对图形的旋转过程中,矩形框随之变动的效果实现非常重要。



It's beautiful , right ? now we add some code to make user can drag & change the size of one canvas.
It's very easy for us to implement that kind of taskes, If mouse clicking hits the tracker (any of the resize buttons, or the middle of the tracker), we must implement tracking to let the user resize or move the existing tracker. This can be easily implemented by calling function CRectTracker::Track():

 1 //--------------------------´¦Àí»­²¼µÄÍ϶¯---------------------------------
 2     if (MouseWithinBitmap(point) == FALSE ) 
 3     {
 4         int nHitTest;
 5         nHitTest=m_trackerSel.HitTest(point);
 6         if(nHitTest == CRectTracker::hitNothing)
 7         {
 8             //µ±Óû§µã»÷µ½»­²¼ÒÔÄÚµÄÇøÓò£¬²»×öÈκÎÓйØTrackerµÄ¶¯×÷£¬Èç¹û×öÁ˵Ļ°£¬¾Í»áÆÁ±ÎÓû§¶Ô
 9             //»­²¼½øÐеÄGDI»­Í¼²Ù×÷
10         }
11         else
12         {
13             //µ±Óû§µã»÷µ½»­²¼ÒÔÍâµÄÇøÓòʱ¡£ÅжÏÊÇ·ñµã»÷µ½hitbutton£¬¼´TrackerÍâΧµÄµã°´Å¥´¦
14             ::ReleaseCapture();
15             switch(nHitTest)
16             {
17                 case CRectTracker::hitRight :
18                     {
19                         ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZEWE));
20                         break;
21                     }
22                 case CRectTracker::hitBottom :
23                     {
24                         ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZENS));
25                         break;
26                     }    
27                 case CRectTracker::hitBottomRight :
28                     {
29                         ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZENWSE));
30                         break;
31                     }
32             }            
33             m_trackerSel.Track(this, point);
34             //ËæÖ®¸Ä±ä»­²¼µÄ´óС£¬ºÍµ±Ç°µÄTracker´óСһÑù¡£
35             m_pDib->ChangeCanvasSize(m_trackerSel.m_rect.Width(),m_trackerSel.m_rect.Height(),LEFT_UP);            
36             Invalidate();
37         }
38     }    
39     //--------------------------´¦Àí»­²¼µÄÍ϶¯---------------------------------

上面的代码在 void CGuanruiPaintView::OnLButtonDown(UINT nFlags, CPoint point)  中得到调用,这样就可以产生当用户拖动Tracker的时候,画布随之变换的效果。



非常酷!呵呵!不知道真正的MS PAINT是如何实现的,反正俺用这种办法,实现了完全相同的功能。一定要把这个翻版的MS PAINT做得逼真到位。 这里有个不完整版,有兴趣的可以下到机器上看看。

https://files.cnblogs.com/nickong/GRPaintLib.rar
https://files.cnblogs.com/nickong/GRPaint.rar

把这两个解开,放在同一目录下,即使没装VC6,也能跑。


(三)关于同时实现两个选择框
非常类似的,当用户点击工具栏中的选择按钮时,用户可以拖动鼠标在画布上绘制一个矩形框。该矩形框没有任何实际作用,仅仅是返回一个CRect对象CRectTrackerInstance.m_rect,让程序知道当前选中的这一块面积。入下图:

关键的窍门就在,只需要在Mousemove事件函数中,对一个CRectTracker对象,也就是图中的小框进行处理,当用户拖动新框的时候小CRectTrack对象.Track()处理其中一个的显示,另外一个,忽略其鼠标拖动事件,不需要给其::SetCapture()。假定对象变量为m_trackerCopy。具体的代码如下:
在Ondraw中
if(!m_trackerCopy.m_rect.IsRectEmpty())
 {
   
}
posted @ 2007-07-20 15:54  RayG  阅读(481)  评论(0编辑  收藏  举报