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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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

posted @ 2020-08-09 11:08  Dincat  阅读(222)  评论(0编辑  收藏  举报