Gribble2 - CGribbleWnd去blbling。
介绍 使用CWnd Gribble1概述的基础在全屏模式。 与BitBlt Gribble2实验 StretchBlt GDI函数,再次WM_PAINT和 WM_ERASEBKGND,发现WM_SYNCPAINT消息。 Gribble2项目 有几个Gribble2的力学变化 CWnd,其中一些来自渴望做一些传送 操作,有些只是为了方便。Gribble2 Gribble1一样,是一个 股票现成的机器基于MFC应用程序向导生成的exe项目 没有文档/视图的支持。一个蛀木水虱在CGribble2App处理菜单项“Go” 创建蛀木水虱窗口: 隐藏,收缩,复制Code
void CGribble2App::OnGribbleGo() { // TODO: Add your command handler code here // Lets create the Gribble window! if(!m_wndGribble.m_hWnd) { CString csWndClass = AfxRegisterWndClass(CS_OWNDC | CS_BYTEALIGNCLIENT, ::LoadCursor(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_CURSOR1)), (HBRUSH)::GetStockObject(BLACK_BRUSH), ::LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_G2ICON))); if(!(m_wndGribble.CreateEx(WS_EX_LEFT, (LPCTSTR)csWndClass, "Gribble Window", WS_VISIBLE|WS_POPUP, 0,0,0,0, NULL, NULL ))) { AfxMessageBox( "Failed to Create Gribble Window)"); return; } } }
在Gribble1, Gribble2注册自己的窗口类提供 这个类的信息窗口的操作系统 维护。再一次,使用CS_OWNDC风格, CS_BYTEALIGNCLIENT风格——这将有助于补充道 以后BitBlt调用的效率。(至少,这是什么 文档说——在实践中,我还没注意到的差异 有或没有,或者CS_BYTEALIGNWINDOW风格。它 可能是使用一个全屏窗口使呢 多余的)。 Gribble1维护自己的光标通过加载的资源 OnCreate方法和压倒一切的CWnd:: OnActivate () 维护它。这里的调用AfxRegisterWndClass告诉 操作系统什么光标与这个类的窗口,当然 光标在应用程序运行时才会有效。这样做 能提供更加简洁的鼠标激活。 Gribble1背景刷的0参数传递和处理的背景 CBrush油漆有自己的成员。Gribble2 分配一个系统加载与GetStockObject刷 窗口类本身。这意味着OnEraseBkgnd 不需要显式地画背景。默认的窗口 程序将使用这个画笔填充背景如果我们不这么做。 最后,现在提供了一个图标窗口。蛀木水虱窗口 没有标题栏,但这个图标将显示当你按Alt + Tab吗 在应用程序之间切换——你可以区分它和 Gribble2应用程序。 设置和清理 的怪癖之一编码窗口包装器类是你经常 处理两个级别的创建和销毁。类本身可以 度过许多创建/毁坏其基本周期窗口,所以 初始化和清理一个通常会联想到 一个c++类的构造函数和析构函数经常被搬到 OnCreate和OnDestroy消息处理程序。 Gribble2遵循这一模式: 隐藏,收缩,复制Code
int CGribbleWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CWnd::OnCreate(lpCreateStruct) == -1) return -1; try { // since this window has its own DC // we can stash it away... // Don't just store a CDC pointer returned // by CWnd::GetDC! m_zeroTrap = m_ScreenDC.Attach( ::GetDC(m_hWnd)); Grb2Fn_InitGribble2Stuff(); // save the taskbar... m_ShellTrayHwnd = ::FindWindow(_T("Shell_TrayWnd"), NULL); if(m_ShellTrayHwnd != NULL) { // ... will we need to set topmost? LONG tb_style = GetWindowLong(m_ShellTrayHwnd, GWL_EXSTYLE); if(!(tb_style & WS_EX_TOPMOST)) { m_ShellTrayHwnd = NULL; // not to worry... } } // Go full screen. SetWindowPos // is more effective // than MoveWindow at obscuring task bar - // but suppress WM_ERASEBKGND SetWindowPos(&wndTopMost, 0,0,m_pixelsX, m_pixelsY, SWP_SHOWWINDOW | SWP_DEFERERASE ); /****************************************************/ /*WARNING DO _NOT_ STICK A BREAK POINT IN THIS AREA */ /****************************************************/ // this creates a memory device context compatible // with our display m_zeroTrap = m_QuadrantsDC.CreateCompatibleDC(&m_ScreenDC); // this creates a bitmap compatible with our display m_zeroTrap = m_QuadrantsBitmap.CreateCompatibleBitmap( &m_ScreenDC, m_pixelsX, m_pixelsY); // note that we don't bother saving the // old bitmap - while their is // a default bitmap associated with a newly created // device context, it is not // really an object that is currently selected // into the DC, and there's // no point in saving it for restoration. (void)m_QuadrantsDC.SelectObject(m_QuadrantsBitmap); // repeat the above for the Extra DC that will // aid in reflecting image regions m_zeroTrap = m_GribbleDC.CreateCompatibleDC(&m_ScreenDC); m_zeroTrap = m_GribbleBitmap.CreateCompatibleBitmap( &m_ScreenDC, m_pixelsX, m_pixelsY); (void)m_GribbleDC.SelectObject(m_GribbleBitmap); } catch(DWORD) { ValidateRect(&m_ScreenRect); CString strMsg; m_zeroTrap.GetMessage(strMsg); // bad place for a messagebox //MessageBox(strMsg, "Error"); TRACE(_T("Error in OnCreate: %s\n"), strMsg); return FALSE; } // used as clip rgn in QuadrantsDC if desired m_QuadCircleRgn.CreateEllipticRgn(m_Q1PointTopLeft.x, m_Q1PointTopLeft.y, m_Q1PointTopLeft.x+m_QuadSize*2, m_Q1PointTopLeft.y+m_QuadSize*2); // Ok - enough stuff available for OnEraseBkgnd to // do its stuff, so send the WM_SYNCPAINT message SendMessage(WM_SYNCPAINT, 0,0 ); // ok - we've forced our window topmost, without altering the // state of the system tray/taskbar thingy - // but if we leave our window topmost, other windows // will be unshowable on this monitor - so lets // get rid of the topmost-ness. Its work here is done... SetWindowPos(&wndNoTopMost, 0,0, m_pixelsX, m_pixelsY, /* SWP_SHOWWINDOW |*/ SWP_DEFERERASE ); /*****************************************************/ /******** END WARNING ( NO BREAK POINTS ) ************/ /*****************************************************/ Grb2Fn_DrawSomethingToGribbleDC(); return 0; }
首先,OnCreate设置一个中心成员对象句柄 windows设备上下文,这是不寻常的windows程序。大多数 窗户没有自己的设备上下文。他们需要画的时候出现 屏幕,他们叫GetDC和给定的直流系统缓存。 然后他们应该叫ReleaseDC直流回到缓存 供其他程序使用。在我们的例子中,我们注册窗口类 CS_OWNDC风格。这意味着设备上下文 不是来自系统缓存,永远为我们有空吗 使用。即使它不是存储在我们的CDC类,访问 通过GetDC将更有效,因为系统 能够提供所有直流比如果去更快 缓存,但是将它附加到疾控中心成员对象的设置 第二个和第三个简单的设备上下文。 接下来,Grb2Fn_InitGribble2Stuff叫做设置 一些屏幕控制指标和初始化变量 模式,将它们分开。 你会看到一个叫FindWindow用来处理的 系统托盘的窗口,即Windows 95任务栏。这是一个的开始 大堆跳铁圈来确保 蛀木水虱真正全屏窗口初始屏幕至少足够长的时间 捕捉到任务栏的背景DCs是免费的。Gribble1没有去这样的长度, 虽然它通常在清算整个屏幕,有 时候没有——让任务栏在屏幕上而重要 蛀木水虱被执行。Gribble2需要更加小心, 因为第一次OnEraseBkgnd,不管的 屏幕将局部背景DCs。 而不是一个简单的调用MoveWindow, Gribble2用途 SetWindowPos。调用SetWindowPos用途 的,wndTopMost价值,这样就使得窗口顶端的 窗口系统中。调用SetWindowPos会导致WM_SYNCPAINT(因此 WM_ERASEBKGND)发送消息。这里使用SWP_DEFERERASE标志 这种情况不会发生,上下文和二次设备 位图OnEraseBkgnd可以操作之前需要设置 正常。 手持屏幕指标 Grb2Fn_InitGribble2Stuff, OnCreate设立第二和第三 设备上下文。这些将被用来做背景的画。的 卡尔的设置设备上下文 类型与我们的屏幕设备上下文相同(现在在 m_ScreenDC)。新的设备上下文不会复制任何GDI对象 选择到源DC中,但是提供默认值。然而, 默认的位图不是我们想为即将到来的 BitBlt/StretchBlt显示,所以代码创建位图 兼容我们的屏幕设备上下文然后选择那些位图到新创建的'compatible' DCs。虽然看起来很无辜,但我们已经做到了 设置一些内容,以便存在于一个DC上的映像可以快速地进行bl汇出 另一方面,为一些非常平滑的图形显示奠定了基础 更新。 接下来,创建一个圆形区域。这将被选择为 如果用户想要查看输出,将区域剪辑到象限DC 这样的。 最后,对SendMessage(WM_SYNCPAINT,0,0)的调用将触发第一个 WM_ERASEBKGND(在WM_NCPAINT之后)和OnEraseBkgnd会做它的事情。这是 被擦除屏幕的初始blits将在此处进行。窗户 文档对确切的WM_SYNCPAINT消息是什么有一点模糊 又是;我把它想成是“嘿,窗户,做你的事,就像我们刚做的那样。 滚动到其他窗口' -即一个方便的方式,以获得适当的流 而不是试图通过发送或 张贴油漆消息或直接调用处理程序。文档状态 这条消息是为Win98及以上,但OnCreate似乎工作正常 Win95。 作为一个边注,很有趣的是看到什么操作系统发送给一个窗口当它 因另一窗口失效: 当另一个窗口出现时,Windows 98发送以下信息到gribble窗口 移过它,或至少为什么我的Win98间谍工具显示作为发送: 隐藏,复制Code
<00001> 00000F98 S WM_NCPAINT hrgn:000009E4 <00002> 00000F98 R WM_NCPAINT <00003> 00000F98 S WM_ERASEBKGND hdc:0000251E <00004> 00000F98 R WM_ERASEBKGND fErased:True <00005> 00000F98 P WM_PAINT hdc:00000000
Windows NT显示如下: 隐藏,复制Code
<00001> 001802B4 S WM_SYNCPAINT <00002> 001802B4 S .WM_NCPAINT hrgn:1D0405AC <00003> 001802B4 R .WM_NCPAINT <00004> 001802B4 S .WM_ERASEBKGND hdc:62010332 <00005> 001802B4 R .WM_ERASEBKGND fErased:True <00006> 001802B4 R WM_SYNCPAINT <00007> 001802B4 P WM_PAINT hdc:00000000
(带“S”的线表示已发送的信息,而带“R”的线表示 返回,“P”代表发布的消息——windows通常使用“post” 在输入队列中给它们一个较低的优先级。) 注意,在NT上,油漆消息嵌套在WM_SYNCPAINT处理中, 这表明它们来自windows默认过程,而不是操作系统。因此, WM_SYNCPAINT消息,至少在NT上,有减少线程间通信的作用 涉及到此绘制消息序列。 而且,我曾经认为WM_ERASEBKGND消息只是作为一个侧面发送的 处理WM_PAINT消息的效果,但它看起来好像这不是真的- 更多的想法在下面。 现在,我在哪里?哦,是的——另一个调用SetWindowPos来删除 最重要的财产-这是重要的!没有这个,我们的窗口就会 隐藏此监视器上的所有其他应用程序。另外,你可能想 避免在此代码中两次调用之间设置断点 SetWindowPos。尤其是当你和单身人士一起工作时 监控机。在这一点上,相信我。有点烦人。如果你真的想做 在这里进行一些探索,至少确保你的任务管理器被设置为“总是” 上”。如果你被困在这里,杀死VC,而不是Gribble,试着杀死 gribble窗口或Gribble2.exe将导致一个效果为消息框 那个TM不能杀死正在调试的进程,这个消息框也不会 是可见的。就像我说的,有点烦人。 这看起来似乎只是为了保持任务而付出了很多努力 事实上,它并不是100%的工作-如果 你从一个非主监视器启动VC,并使用断点 在OnCreate上,你可能会看到任务栏。这还不算太糟, 真的。我真正想避免的是有一个位图的 任务栏在屏幕上(烦人)。 最后,我调用一个函数来绘制一些次要的东西 背景DC (gribble DC),完成了。 这样清理是明智的: 隐藏,收缩,复制Code
void CGribbleWnd::OnDestroy() { CWnd::OnDestroy(); // Cleanup compatibles m_QuadrantsDC.DeleteDC(); m_GribbleDC.DeleteDC(); m_QuadrantsBitmap.DeleteObject(); m_GribbleBitmap.DeleteObject(); m_QuadCircleRgn.DeleteObject(); // detatch the HDC we got from ::GetDC. // Note that since we are using a private DC there is // no need to Release the DC - it does not // come from the DC cache, unless we ask for one // from the cache by using GetDCEx with the // DCX_CACHE flag set. m_ScreenDC.Detach(); m_bErased = false; // if necessary, restore the topmost property of // the taskbar... if(m_ShellTrayHwnd != NULL) { TRACE(_T("Setting tray window to top\n")); ::SetWindowPos(m_ShellTrayHwnd, HWND_TOPMOST, 0,0,0,0, SWP_NOMOVE); } }
注意,我们不需要从它们中选择位图 各自的DCs -位图不同于大多数的“可选”对象 考虑到这一点,我们的清理工作变得相当简单。只要删除DCs, 位图和我们在OnCreate中创建的区域。 我们不需要删除主DC,因为它是窗口的一部分 我们将分离它,这样CDC对象的析构函数就不会 感到困惑。此外,这是重要的注意,如果你的新 这个设备上下文的东西,我们不需要调用 ReleaseDC又是; 你会读到很多关于设备的文章 上下文,它会告诉你你处理WM_PAINT消息 调用BeginPaint(它调用GetDC),做你的绘画,和 调用EndPaint(它调用ReleaseDC)。如果你正在做你的 渲染外部的上下文WM_PAINT消息,你可以调用 GetDC ReleaseDC。点了, 正确的是,当GetDC在这些情况下被调用时,您 从系统缓存中接收一个DC,它是一个被应答的资源 完成后再替换。不释放从系统获取的直流电源 缓存是一个严重的禁忌,它会导致系统范围内的资源消耗。 然而,这个gribble窗口有它自己的关联的设备上下文 它。它不是来自系统缓存。事实上,你应该看到非常 对Win98资源计量表中的GDI资源栏影响很小 运行Gribble2.exe时的实用程序。但你不想加上这个 OWN_DC样式到你所有的窗口和控件。关联设备是 一大堆东西,其中一些(比如字体)可以 占用大量内存。我想说的是 gribble window背离了传统,但这是有意的 作为一个特殊的全屏幕窗口,我希望我在描述规则 好到你能明白我为什么要打破它们。 最后,如果创建时任务栏“总是在顶部” 窗口,我们第一次调用SetWindowPos就会抢劫它 它的最高状态,我们会很好地恢复它。如果我们不, 当Gribble2.exe退出后,任务栏将出现在屏幕上 用户将能够与其他窗口遮蔽它,这可能不是 我们发现这个系统时的样子。如果m_ShellTrayWnd NULL,这意味着任务栏没有设置最顶层样式位 我们在OnCreate登记过,所以不吃香肠。 OnEraseBkgnd 关于正确使用WM_ERASEBKGND,似乎存在着不同的哲学 以及windows编程中的WM_PAINT处理程序。唯一的 目前为止为Gribble1提供的线程适用于此-为什么我们要 有两个不同的消息发送到我们的窗口,本质是什么 相同的任务吗?当我们可以在一个处理器中完成所有的工作时,为什么要在两个处理器中编写代码呢? 在我们的gribble windows案例中这个设置是 方便的一个。但是让我们快速看看这些“绘画”是什么 先来了解一下原因。 windows操作系统知道已经创建的所有windows。它 它知道他们什么时候睡着,什么时候醒着,是否睡着 他们是好是坏,等等。更重要的是,它将采取行动 他们变得无效。窗口可能会变得无效(或者,也许更多 点,一个区域或矩形窗口可以成为无效)时,我们 通过调用InvalidateRect或InvalidateRgn,或显式地使其如此 当另一个窗口通过显示使该窗口的全部或部分无效时 越过它,移动或关闭。 在我们显式地使窗口无效的情况下,我们有一些 WM_ERASEBKGND消息是否将被发送的控制程度 的布尔参数 InvalidateRect和InvalidateRgn调用。 实际上,这个参数更多的是一个提示——如果其他区域是 对背景擦除进行指定后,将发送WM_ERASEBKGND消息 当BeginPaint消息被调用时。我们可以检查一下 调用填充的PAINTSTRUCT中的布尔值标记 在它返回后开始绘制。 这是windows应用程序处理 WM_PAINT消息,如上所述。开始画画的召唤 还返回设备上下文句柄,验证更新区域,和 在执行绘制时隐藏插入符号(如果需要)。一个 如果插入符号被隐藏,则调用EndPaint恢复插入符号 BeginPaint,并释放关联设备。 WM_ERASEBKGND也会在消息中被系统发送到我们的应用程序 当一个窗口被另一个窗口失效时,用来告诉它自己进行绘制的序列 窗口或通过显式的WM_SYNCPAINT消息。也就是说,WM_NCPAINT, WM_ERASEBKGND和WM_PAINT (post)。 事实证明,这是一件好事,如代码所示 下图: 隐藏,收缩,复制Code
BOOL CGribbleWnd::OnEraseBkgnd(CDC* pDC) { // the first version of this window used its // own brush to erase the background // - this version passes a background // brush to AfxRegisterWndClass in OnGribbleGo, // so we can // concentrate on other stuff. //TRACE(_T("Inside OnEraseBkgnd...\n")); // Lets assure ourselves that the HDC OnEraseBkgnd // is handing us is the same as our private one - // if so we can assume that // the MFC is respecting our privacy in this regard. VERIFY(pDC->m_hDC == m_ScreenDC.m_hDC); if(!m_bErased) { // here, we only actually erase once, // at the start. // Since the window does have its own brush, // returning 0 would probably have // the same effect, but we want to do some init. int ret = CWnd::OnEraseBkgnd(&m_ScreenDC); // blt to the compat... in effect, // clear our working DC as well try { // wipe both working DCs - allows for testing // of new tricks etc... // Win9x users - comment out the call to // m_GribbleDC for an interesting effect! m_zeroTrap = m_QuadrantsDC.BitBlt(0, 0, m_pixelsX,m_pixelsY, &m_ScreenDC, 0, 0, SRCCOPY); m_zeroTrap = m_GribbleDC.BitBlt(0, 0, m_pixelsX,m_pixelsY, &m_ScreenDC, 0, 0, SRCCOPY); } catch(DWORD) { ValidateRect(&m_ScreenRect); CString strMsg; m_zeroTrap.GetMessage(strMsg); MessageBox(strMsg, "Error"); return FALSE; } // select special clip region, if desired if(m_bUseCircle) { m_QuadrantsDC.SelectClipRgn(&m_QuadCircleRgn, RGN_COPY); } m_bErased = true; return ret; } else { // only erase the background if another window is // being dragged over us - // better yet, just blit the screen bitmap - // and our window just stays the way it is - // very smooth! if(GetForegroundWindow() != this) { m_ScreenDC.BitBlt(0, 0, m_pixelsX,m_pixelsY, &m_QuadrantsDC, 0, 0, SRCCOPY ); } return true; } }
那么,这里发生了什么?第一次进入这个函数时 (间接通过SendMessage(WM_SYNCPAINT,0,0) (调用OnCreate)我们通过调用擦除背景 CWnd:: OnEraseBknd。实际上,我们可以返回false和 获得相同的结果(默认窗口proc将使用类 背景刷擦去背景),但我们想做最后一个 这里有一些设置。从CWnd:::OnEraseBknd回来后, 我们想要复制新抹掉的屏幕到我们的背景设备 上下文。现在我们可以重新开始工作了。 注意,它也是在这个“一次仅”处理,即 主要背景DC的裁剪区域(我称之为 象限DC)被设置为在OnCreate中设置的圆形区域 设置布尔。 在gribble窗口有焦点的时期,绘画将会 值为的InvalidateRect调用触发 对于bErase参数,为FALSE,因此 如果我们呼叫,机器人不应该开火 BeginPaint。但是,如上所述,WM_SYNCPAINT类型 使gribble窗口无效时发生的消息序列 另一个人也可以把我们送到这里,我打个电话给 获得foregroundwindow来确定(几乎是给定的)我们是否 确实是在处理我们无法控制的力量。(注意不是所有的 窗口可以使用这样一个简单的测试——这肯定有助于成为一个完整的 纱窗!)所有OnEraseBkgnd需要做 情况是位块传输的主要背景直流屏直流和位图 我们完成了!我们的窗口与绝对的无效区域更新 最小的闪烁和其他kafuffle自然。这是 漂亮光滑只试一试,你会喜欢它的! OnPaint 现在,女士们,绅士,可爱的和有才华的OnPaint。 隐藏,收缩,复制Code
void CGribbleWnd::OnPaint() { //CPaintDC dc(this); // thanks, we already got one... // since we are using a private device context, // and not calling BeginPaint // (by way of CPaintDC), we are responsible // for validating the affected // area - if we don't do this, the system will // continue to send us WM_PAINT // messages. Also, this seems to work only // if we validate the whole screen, // even though we called InvalidateRect on a // smaller portion. ValidateRect(&m_ScreenRect); // if we're not just painting because some nasty // window is dancing the Macarena over us... if(GetForegroundWindow() == this) { // call whatever nifty paint function you got... // the gribble DC is drawn to at start and on // left click Grb2Fn_BlitGribbleToQuadrantsDC(); } // ...then transfer the artwork wholesale // to our screens DC try { m_zeroTrap = m_ScreenDC.BitBlt(0, 0, m_pixelsX,m_pixelsY, &m_QuadrantsDC, 0, 0, SRCCOPY ); } catch(DWORD) { ValidateRect(&m_ScreenRect); CString strMsg; m_zeroTrap.GetMessage(strMsg); MessageBox(strMsg, "Error"); } if(GetFocus()==this) { Sleep(m_nSpeed); InvalidateRect(&m_KaleideRect, FALSE); } // Do not call CWnd::OnPaint() // for painting messages }
点注意:首先,我们不需要任何发臭的没有 需要调用BeginPaint。没有隐藏,我们插入符号 有自己的直流玩,由于只 然而,我们需要验证我们的窗口。注意,您可以 sort-of-kind-of侥幸不调用ValidateRect,但它 意味着操作系统将与WM_PAINT不断骚扰您的应用程序 消息。不是一件好事。尽管WM_PAINT消息通常 发布到线程和一个低优先级,有盈余 输入队列的线程将使其他消息很难 获得通过。调用睡眠可以帮助一点,因为当线程醒来 的重要信息会得到应有的尊重,但它的 还好建议在WM_PAINT狼吞虎咽地验证窗口 处理程序。 OnEraseBkgnd不会叫,不是因为我们设置bErase 为假时失效,但因为我们没有使用BeginPaint。( CPaintDC对象的创建注释掉了会导致BeginPaint 被称为)。 接下来,我们调用一个方法吸引任何更新,我们需要 主要背景兼容直流(建立在OnCreate和清除 初始调用OnEraseBkgnd)然后位块传输 屏幕。 然而,它仍然很高兴决定,我们在OnEraseBknd, 我们是否被要求油漆新东西(我们自己的 InvalidateRect)或在回应一些基于VB的臃肿 牛的应用垂涎我们的房地产,所以我把 在这里GetForegroundWindow打电话。实际上使 在这种情况下,调用BitBlt可能过分了。如果一个 蛀木水虱窗口WM_PAINT消息到达时没有关注, 很可能OnEraseBkgnd所做的位块传输的工作。请注意 这叫ValidateRect OnEraseBkgnd内部 在这种情况下不会抑制的WM_PAINT消息发布 结束WM_SYNCPAINT类型的消息流。 最后,如果我们有重点,睡眠允许用户调用慢的事情 (1秒的马克斯,考虑到属性对话框的限制) 我们称之为InvalidateRect再次开始。你可能想要改变 使用一个计时器,为屏幕保护程序的正常方式 等。使用睡眠使应用程序不响应,但是 无效是方便的,如果我们失去时验证矩形 专注,或单击右键,或异常处理程序,我们停止 这个过程没有再费周折又是; 蛀木水虱我这,Blitman ! 所以,这些传送东西要完成什么?很高兴的你 问只血腥令人惊叹的你还看在这一点上, actually 好吧,本来我的想法是做一个 万花筒。真的。但现实的万花筒需要 智能旋转图像的能力 非平凡的方式,良好的旋转变换 NT上可用,而不是Windows 9 x,所以我决定……嗯…好吧, 无论什么。称之为Gribeidoscope我猜…… 所有有趣的东西发生在两个CGribbleWnd成员 函数,Grb2Fn_DrawSomethingToGribbleDC和Grb2Fn_BlitGribbleToQuadrantsDC又是; Grb2Fn_DrawSomethingToGribbleDC绘画,顾名思义,一个东西 蛀木水虱直流。这是背景直流背后的真实背景,我 象限直流。Grb2Fn_DrawSomethingToGribbleDC分裂蛀木水虱广场 成两个三角形,反映出每一项由交换x和y坐标 和交替地设置每个三角形作为蛀木水虱剪辑区域。 这允许反射对角线上解剖广场,这是 不可用的简单的翻转x和y轴上的可用 StretchBlt函数。 然后,在OnPaint, Grb2Fn_BlitGribbleToQuadrantsDC叫做,开始 左上角,并执行三个StretchBlt调用复制一个 四分之一的蛀木水虱广场的左上象限广场 (直流),然后反映广场到左下象限, 向右一半的反映,我们有gribeidosopic 又是生效; Grb2Fn_DrawSomethingToGribbleDC叫做当蛀木水虱窗口第一次 创建,当用户鼠标左键在窗口。(你得左点击 前几次蛀木水虱变得有趣。) 隐藏,收缩,复制Code
/* *********************************************** For a Kaleidescopic effect, we would really want to flip a portion of an existing image (1) along its diagonal, to generate the first reflection. All we can do with the Win9x API is flip on the X or Y axis - simulating a flip on the hypotenuse would require one h flip and a rotation. With Win NT/2000 rotations are available - but not Win9x. So, we'll fake it by trying to do the 'flip' in the rendering to the gribble DC - essentially, draw everthing twice... - - - - - - - - - - - - - - | \ | | \ | | \ rgn2 | | \ | | \ | | \ | | \ | | \ | | rgn1 \ | | \ | | \ | | \ | - - - - - - - - - - - - - - *********************************************/ BOOL CGribbleWnd::Grb2Fn_DrawSomethingToGribbleDC() { BOOL retval = TRUE; CRgn rgn1, rgn2; // for clipping POINT pPoints1[3] = {m_KaleideRect.left, m_KaleideRect.top, m_KaleideRect.left, m_KaleideRect.bottom, m_KaleideRect.right, m_KaleideRect.bottom }; POINT pPoints2[3] = {m_KaleideRect.left, m_KaleideRect.top, m_KaleideRect.right, m_KaleideRect.top, m_KaleideRect.right, m_KaleideRect.bottom }; rgn1.CreatePolygonRgn(pPoints1, 3, ALTERNATE); rgn2.CreatePolygonRgn(pPoints2, 3, ALTERNATE); // get ready to draw, podner... srand( (unsigned)time( NULL ) ); COLORREF clr = RGB(rand()%256, rand()%256, rand()%256); static POINT pts[4096]; for (int i = 0; i < 4096; i++) { pts[i].x = rand()%(m_QuadSize*2); pts[i].y = rand()%(m_QuadSize*2); } // lines, triangles and dots - oh my! switch(m_gribbleType) { case dots: m_GribbleDC.SelectClipRgn(&rgn1, RGN_COPY); for (i = 0; i < m_nDots; i++ ) { m_GribbleDC.SetPixel ( m_KaleideRect.left + pts[i].x, m_KaleideRect.top + pts[i].y, clr); } m_GribbleDC.SelectObject(&rgn2); for (i = 0; i < m_nDots; i++ ) { m_GribbleDC.SetPixel ( m_KaleideRect.left + pts[i].y, m_KaleideRect.top + pts[i].x, clr); } break; case lines: { CPen pen(PS_SOLID, 0, clr); CPen *pOldpen = m_GribbleDC.SelectObject(&pen); POINT p[2]; m_GribbleDC.SelectClipRgn(&rgn1, RGN_COPY); for (i = 0; i < m_nLines*2; i+=2 ) { p[0].x = m_KaleideRect.left + pts[i].x; p[0].y = m_KaleideRect.top + pts[i].y; p[1].x = m_KaleideRect.left + pts[i+1].x; p[1].y = m_KaleideRect.top + pts[i+1].y; m_GribbleDC.Polyline(p,2); // the LineTo call was easier on the eyes, // but resulted in some unwanted // horizontal lines in the output... // m_GribbleDC.LineTo(m_KaleideRect.left + // pts[i].x,m_KaleideRect.top + pts[i].y); } m_GribbleDC.SelectClipRgn(&rgn2, RGN_COPY); for (i = 0; i < m_nLines*2; i+=2 ) { // there is an xy swap going on here p[0].x = m_KaleideRect.left + pts[i].y; p[0].y = m_KaleideRect.top + pts[i].x; p[1].x = m_KaleideRect.left + pts[i+1].y; p[1].y = m_KaleideRect.top + pts[i+1].x; m_GribbleDC.Polyline(p,2); } m_GribbleDC.SelectObject(pOldpen); } break; case triangles: { // much like lines, but with 3 points to worry about... CPen pen(PS_SOLID, 0, clr); CBrush brsh(clr); CPen *pOldpen = m_GribbleDC.SelectObject(&pen); CBrush *pOldbrsh = m_GribbleDC.SelectObject(&brsh); POINT p[3]; m_GribbleDC.SelectClipRgn(&rgn1, RGN_COPY); for (i = 0; i < m_nTriangles*3; i+=3 ) { p[0].x = m_KaleideRect.left + pts[i].x; p[0].y = m_KaleideRect.top + pts[i].y; p[1].x = m_KaleideRect.left + pts[i+1].x; p[1].y = m_KaleideRect.top + pts[i+1].y; p[2].x = m_KaleideRect.left + pts[i+2].x; p[2].y = m_KaleideRect.top + pts[i+2].y; if(m_bConstrain) { Grb2Hlp_ForcePointInRgn(&rgn1, p[0]); Grb2Hlp_ForcePointInRgn(&rgn1, p[1]); Grb2Hlp_ForcePointInRgn(&rgn1, p[2]); } m_GribbleDC.Polygon(p,3); } m_GribbleDC.SelectClipRgn(&rgn2, RGN_COPY); for (i = 0; i < m_nTriangles*3; i+=3 ) { // there is an xy swap going on here p[0].x = m_KaleideRect.left + pts[i].y; p[0].y = m_KaleideRect.top + pts[i].x; p[1].x = m_KaleideRect.left + pts[i+1].y; p[1].y = m_KaleideRect.top + pts[i+1].x; p[2].x = m_KaleideRect.left + pts[i+2].y; p[2].y = m_KaleideRect.top + pts[i+2].x; if(m_bConstrain) { Grb2Hlp_ForcePointInRgn(&rgn2, p[0]); Grb2Hlp_ForcePointInRgn(&rgn2, p[1]); Grb2Hlp_ForcePointInRgn(&rgn2, p[2]); } m_GribbleDC.Polygon(p,3); } m_GribbleDC.SelectObject(pOldpen); m_GribbleDC.SelectObject(pOldbrsh); } break; default: break; } // note that SelectClipRgn with RGN_COPY := SelectObject, // but for nMode param - // we could use SelectObject(&rgn1) here with the // same result. return retval; }
Grb2Hlp_ForcePointInRgn函数用于三角形适合一个地区。 你可以试试这种效应通过选择限制复选框的属性 对话框。 隐藏,复制Code
// expects a region which is a half square split // on the diagonal. // any point in the square that is not in the region in // question can be mapped into it by swapping x and y. // DrawSomethingToGribbleDC uses this to stop the spread of // mutant blobby triangles void CGribbleWnd::Grb2Hlp_ForcePointInRgn(CRgn *rgn, POINT& p) { if(!rgn->PtInRegion(p)) { // swap x and y register int x,y; x = p.x - m_Q1PointTopLeft.x; // strip offset y = p.y - m_Q1PointTopLeft.y; p.x = m_Q1PointTopLeft.x + y; p.y = m_Q1PointTopLeft.y + x; } }
Grb2Fn_BlitGribbleToQuadrantsDC副本有没有变化g蛀木水虱的一部分 广场的象限直流给运动的影响。这就是所谓的在 OnPaint当我们有焦点。 隐藏,收缩,复制Code
/* ************************************************** Imagine a square divided into four quadrants: - - - - - - - - - - - - - - Q1 | | | | | | | | | | | | | | | H1 { | - - - - - - - - - - - - | } H2 | | | | | | | | | Q4 | | | | | | | | | - - - - - - - - - - - - - - 1. StretchBlt some area of the Gribble DC into Q1. 2. Flip Q1 into Q4 with a 1 to 1 StretchBlt, mirroring y axis. 3. Flip H1 into H2 with a 1 to 1 StretchBlt, mirroring x axis. // *************************************************** */ BOOL CGribbleWnd::Grb2Fn_BlitGribbleToQuadrantsDC() { BOOL retval = TRUE; try { // blit stuff to Q1 - we can affect the // whole reflection here m_zeroTrap = m_QuadrantsDC.StretchBlt( m_Q1PointTopLeft.x, m_Q1PointTopLeft.y, m_QuadSize, m_QuadSize, &m_GribbleDC, m_Q1PointTopLeft.x + m_nBlitPos, m_Q1PointTopLeft.y + m_nBlitPos, m_QuadSize/m_nStretchX, m_QuadSize/m_nStretchY, SRCCOPY ); // stretch blit Q1 into Q4... m_zeroTrap = m_QuadrantsDC.StretchBlt( m_Q1PointBottomLeft.x, m_Q1PointBottomLeft.y, m_QuadSize, m_QuadSize, &m_QuadrantsDC, m_Q1PointBottomLeft.x, m_Q1PointBottomLeft.y-1, m_QuadSize, -m_QuadSize, // mirror Y SRCCOPY ); // then flip H1 to H2. m_zeroTrap = m_QuadrantsDC.StretchBlt( m_Q1PointTopRight.x, m_Q1PointTopRight.y, m_QuadSize, m_QuadSize*2, &m_QuadrantsDC, m_Q1PointTopRight.x-1, m_Q1PointTopRight.y, -m_QuadSize, m_QuadSize*2, // mirror X SRCCOPY ); // if we've travelled halfway down the diagonal, // or gone back to the top, reverse direction. m_nBlitPos += m_nDirection; if(m_nBlitPos == m_QuadSize) { m_nDirection = -1; } else { if(m_nBlitPos == 0) { m_nDirection = 1; } } } catch(DWORD) { ValidateRect(&m_ScreenRect); CString strMsg; m_zeroTrap.GetMessage(strMsg); MessageBox(strMsg, "Error"); retval = FALSE; } return retval; }
Misc 有一个属性对话框实现允许用户选择类型 蛀木水虱的使用(点、线、或三角形)和其他设置。 当前设置存储在注册中心在HKCU \ \蛀木水虱\ Gribble2软件。 错误处理 我注意到,大多数GDI调用返回0为失败,这一些 (但不是全部)需要调用每个盘来确定 失败的原因。我已经厌倦了做每一个错误检查 电话,所以我创建了一个名为CZeroResultTrap的小类 他唯一的人生目标就是抓住过去的错误,抛出一个吗 例外,如果它被赋予一个0。如果每个盘调用返回0,它会 报告一个通用的消息。我把它在错误检查较少的干扰,但是 不建议你冲出去在生产应用程序中使用它。它也 有趣的,一些不失败——选择一个GDI调用 默认的(股票)对象与MM_TEXT映射模式,屏幕直流 例子中,或面板选择,没有内存需求。 如果你想看到我偷了,信息看到的地方 文章“GDI对象”在MSDN。 总结 这个蛀木水虱图形的表现不会赢得任何 奖,数学(如果你可以称呼它)非常简单。我 希望这篇文章是有用的讨论windows油漆 信息和如何处理它们,也许一些简单的传送 的想法。我不认为我已经穷尽了所有问题,所以检查 火焰本文在赌你的反馈 在这些技术又是工资; 蛀木水虱快乐 本文转载于:http://www.diyabc.com/frontweb/news4916.html