【问题标题】:Freehand Drawing on Entire Screen整个屏幕上的手绘
【发布时间】:2011-11-28 03:08:34
【问题描述】:

我正在尝试制作一个简单的小工具,允许用户从正常操作切换到禁用所有应用程序消息的模式,他们可以使用鼠标进行一些手绘,然后再次切换模式以保持他们在屏幕上画画,同时他们做任何他们想做的其他正常的事情。如果我决定,这可能会演变成一件好事,您可以通过保存您所做的装饰并稍后加载它们来使用装饰屏幕。

当我开始这个时(半年多以前,在发现 Windows API 后不久),我只是进行了全局鼠标跟踪,并在 GetDC(NULL) hdc 的任何位置画了一个圆圈。当然,问题是当它下面的任何东西更新时它会消失并且仍然有鼠标消息通过,所以如果我按住桌面上的按钮,例如,它会在整个绘画中调整矩形的大小。

今天,自从 6 个月前的最后一次主要工作以来,我终于有了一些空闲时间,我决定重新制作它,看看我是否能达到我想要的效果。我制作了一个透明的、最顶层的、WS_CHILD、分层的、最大化的窗口(基本上屏幕不会改变,但在所有东西的顶部都有一个窗口让消息通过)。接下来,我做了它,当它处于绘画模式时,它会将 alpha 值设置为 1 并让用户绘画。直到我这样做之前我才意识到,由于窗口的 alpha 值为 1,所以没有任何绘画是可见的。

接下来,我尝试使用 GetDC(NULL),但记得更新时它会被删除。

现在我只是想使用位图和 dcs 将屏幕重复存储到 dc 中,在另一个 dc 上绘制,然后将其复制回存储的屏幕,其中未绘制的部分具有透明度,并将其复制回屏幕,但我正在失去一点心。这是我的源代码(掩码函数取自this tutorial)。请告诉我其中一些是否不必要。我确实使用位图进行双缓冲,但我不确定我需要它们的位置。

//Global mask since it takes longer to make
HBITMAP mask;

//Window Procedure Start
HDC screenDC; //hdc for entire screen
screenDC = GetDC (NULL); //get DC for screen

HDC memDC = CreateCompatibleDC (screenDC); //create DC for holding the screen+paint
HBITMAP bm = CreateCompatibleBitmap (screenDC, GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN)); //create bitmap for memDC

HDC paintDC = CreateCompatibleDC (screenDC); //create DC to paint on
HBITMAP paintBM = CreateCompatibleBitmap (screenDC, GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN)); //create bitmap for paintDC

SelectObject (memDC, bm); //select bitmap into memDC
SelectObject (paintDC, paintBM); //select painting bitmap into paintDC
BitBlt (memDC, 0, 0, GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN), screenDC, 0, 0, SRCCOPY); //copy screen to memDC
SetBkColor (paintDC, RGB(0,0,0)); //set background of paintDC to black so it's all transparent to start

//WM_CREATE
mask = CreateBitmapMask (bm, RGB(0,0,0)); //create black mask (paint colours are limited 1-255 now)

//painting is done into paintDC

//at end of Window Procedure
SelectObject (paintDC, mask); //select mask into paintDC
BitBlt (memDC, 0, 0, GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN), paintDC, 0, 0, SRCAND); //this in combination with the next should make it bitblt with all of the black taken out I thought
SelectObject (paintDC, paintBM); //select bitmaps into DCs
SelectObject (memDC, bm);
BitBlt (memDC, 0, 0, GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN), paintDC, 0, 0, SRCPAINT); //second part of transparent bitblt
BitBlt (screenDC, 0, 0, GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN), paintDC, 0, 0, SRCCOPY); //copy memDC back to screen

DeleteObject (paintBM); //delete stuff
DeleteObject (mask);
DeleteDC (memDC);
DeleteDC (paintDC);
ReleaseDC (hwnd, screenDC);

//CreateBitmapMask() (taken directly from http://www.winprog.org/tutorial/transparency.html
HBITMAP CreateBitmapMask(HBITMAP hbmColour, COLORREF crTransparent)
{
HDC hdcMem, hdcMem2;
HBITMAP hbmMask;
BITMAP bm;

// Create monochrome (1 bit) mask bitmap.

GetObject(hbmColour, sizeof(BITMAP), &bm);
hbmMask = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NULL);

// Get some HDCs that are compatible with the display driver

hdcMem = CreateCompatibleDC(0);
hdcMem2 = CreateCompatibleDC(0);

SelectObject(hdcMem, hbmColour);
SelectObject(hdcMem2, hbmMask);

// Set the background colour of the colour image to the colour
// you want to be transparent.
SetBkColor(hdcMem, crTransparent);

// Copy the bits from the colour image to the B+W mask... everything
// with the background colour ends up white while everythig else ends up
// black...Just what we wanted.

BitBlt(hdcMem2, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);

// Take our new mask and use it to turn the transparent colour in our
// original colour image to black so the transparency effect will
// work right.
BitBlt(hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem2, 0, 0, SRCINVERT);

// Clean up.

DeleteDC(hdcMem);
DeleteDC(hdcMem2);

return hbmMask;
}

我知道代码可能很糟糕。我正在考虑所有建议,只是我对这个主题不太清楚,并且没有得到正在发生的一切,这使得它很难修复。这段代码通过几乎每隔一段时间放置一个全屏黑色矩形来为我运行。

我的主要问题是:有没有什么方法可以在下面的窗口更新时在屏幕上绘画而不会被擦除?我现在能想到的唯一真实的事情就是存储用户绘制的细线段的所有位置,并在屏幕顶​​部不断地重新绘制它们。乍一看似乎效率很低,而且很浪费内存。

另外,我在写这篇文章时很确定,在提供的代码段之前,我不需要任何代码示例来用于理论内容。现在大部分都消失了,但这确实是一个理论问题。

编辑: 我刚刚发现了似乎非常适合这种情况的 TransparentBlt 功能,所以我尝试使用它而不是 SRCPAINT 和 SRCAND BitBlts,它产生了相同的结果:一个黑色的矩形覆盖屏幕,有时当我的鼠标移动时有部分消失东西。

【问题讨论】:

    标签: c++ winapi bitmap gdi


    【解决方案1】:

    也许是最简单的方法:

    在非绘图模式下,使用SetLayeredWindowAttributes 为透明窗口设置“透明键”颜色。使窗口的 alpha 完全不透明,但用该键颜色填充窗口(FillRect 或类似的),它将全部显示为透明。然后,您以非关键颜色绘制的任何内容都将显示为纯色,位于透明分层窗口下方的所有窗口之上。

    要进入绘图模式,一种方法是在透明层下创建一个新窗口,其中包含捕获的桌面位图。或者避免使用位图,使其略微不透明并且全部为纯色 - 例如,看起来桌面“变灰”。关键是这个窗口不是完全透明的,所以它可以接收鼠标输入,然后你可以用它在实际的透明层上绘图。

    【讨论】:

    • 乍一看,这个想法似乎简单而精彩!从来没想过用2个分层的窗口来达到我想要的效果,让桌面变灰变得简单。我使用 LWA_ALPHA 的唯一原因是我可以捕获鼠标消息,因为 LWA_COLORKEY 不会,但是下面的另一个窗口可以解决所有问题。我可能会因为功课太忙而无法尝试几天,但我会告诉你它是如何工作的。
    【解决方案2】:

    我认为最好先创建屏幕快照并将其保存在位图中(以内存 DC 的形式),然后再显示一个全屏显示内存 DC 内容的窗口。这样,您实际上就可以在自己的窗口上获取由点击等引起的消息,并像往常一样处理它们。

    1. 捕获屏幕内容
    2. 创建窗口(全屏)并使用捕获的内容
    3. 画图
    4. 保存内容(为 bmp 或其他任何形式)
    5. 关闭窗口并返回常规桌面

    好主意?

    【讨论】:

    • 我看到的一个问题(除非我想错了)是他们切换回可以做其他所有事情的地方后,我无法保留他们所画的东西(即这有相同的效果作为透明度为 1 的分层窗口,除了它下面的东西在我绘画时实时更新。如果这不是你的意思,请纠正我。当我回头看位图时,我想保存/ load 是paintBM - 带有用户绘图的那个。然后可以将其放在带有屏幕图像的“顶部”上,然后可以将其应用回屏幕。
    • 将绘图保存为捕获屏幕顶部的图层是没有问题的。但是,如果我理解正确,您希望屏幕在您绘制时更新?
    • 有点,主要是在模式切换后能够让绘画保持不变(如果有人想知道,这是通过我的窗口处理的热键完成的)。我能想到的最终解决方案似乎是不可能的,但在顶部有一个背景透明的窗口,但在那之后没有任何东西。这将导致窗口停留在那里,可见油漆粘在上面,而任何未上漆的部分都将不可见。如果可能的话,我会很高兴,但我没有找到类似的东西:/
    • 在这种情况下,您有两条路线: 1. 创建一个常规窗口,捕获其下方的屏幕并每隔 100 毫秒更新一次。或者 2. 您创建一个分层窗口,但不是将整个窗口的 alpha 设置为 1,而是为每个像素使用 alpha 通道。我认为在这种情况下选项 2 会更合适。但是您必须弄清楚如何在分层窗口上使用每个像素的 Alpha 通道透明度。然后,所有绘图都可以存储在内存 DC(每个像素的 Alpha 通道)中,并且在每个 WM_PAINT 上,您只需 AlphaBlend 将其存储到主窗口。
    • 挂钩是不必要的,记住在使用AlphaBlend函数时,颜色值应该预先乘以alpha值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-08
    • 2018-04-26
    • 1970-01-01
    • 2018-06-08
    • 1970-01-01
    • 2010-09-25
    相关资源
    最近更新 更多