整个屏幕上的写意画

时间:2011-09-30 01:00:46

标签: c++ winapi bitmap gdi

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

当我开始这个(超过半年之后,发现Windows API之后不久)我刚刚进行了全局鼠标跟踪并在GetDC(NULL)hdc的任何地方画了一个圆圈。当然,问题在于当它下面的任何内容更新并且仍然会传递鼠标消息时它会消失,所以如果我按下桌面上的按钮,例如,它会在整个绘画中调整矩形的大小。

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

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

现在我只想使用位图和dcs将屏幕重复存储到直流,在另一个直流上绘画,然后将其复制回具有存储屏幕的屏幕,其中未绘制的部分具有透明度,并将其复制回屏幕,但我失去了一点心。这是我的源代码(掩码函数取自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,它产生了相同的结果:一个覆盖屏幕的黑色矩形,有时当我的鼠标移动时部分会消失的东西。

2 个答案:

答案 0 :(得分:3)

最简单的方法,也许:

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

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

答案 1 :(得分:0)

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

  1. 捕获屏幕内容
  2. 创建窗口(全屏)并使用捕获的内容
  3. 做一些绘画
  4. 保存内容(如bmp或任何花哨的东西)
  5. 关闭窗口并返回常规桌面
  6. 好主意?