1

This is a continuation of my other question (understanding of it is not required, just for reference):

It turns out that Graphics.CopyFromScreen which I'm using has the following artefact - it assumes I want to copy with transparency key = Color.Black, so it skips those very dark areas - part of my desktop background image - they suddenly appear in white, and now it looks rather ugly:

enter image description here

The code I'm currently using is provided below:

private static Bitmap MakeScreenshot()
{
  Rectangle bounds = Screen.GetBounds(Point.Empty);
  Bitmap image = new Bitmap(bounds.Width, bounds.Height);

  using (Graphics g = Graphics.FromImage(image))
  {
    g.CopyFromScreen(Point.Empty, Point.Empty, bounds.Size);
  }

  return image;
}

An answer by Hans Passant suggested using a p/invoke instead, but for a different issue - layered windows - so I tried that, and it worked, below is equivalent code, but now using WinApi:

public static Bitmap CreateBitmapFromDesktopNew()
{
  Size sz = GetDesktopBounds().Size;
  IntPtr hDesk = GetDesktopWindow();
  IntPtr hSrce = GetWindowDC(hDesk);
  IntPtr hDest = CreateCompatibleDC(hSrce);
  IntPtr hBmp = CreateCompatibleBitmap(hSrce, sz.Width, sz.Height);
  IntPtr hOldBmp = SelectObject(hDest, hBmp);
  bool b = BitBlt(hDest, 0, 0, sz.Width, sz.Height, hSrce, 0, 0, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt);
  Bitmap bmp = Bitmap.FromHbitmap(hBmp);
  SelectObject(hDest, hOldBmp);
  DeleteObject(hBmp);
  DeleteDC(hDest);
  ReleaseDC(hDesk, hSrce);
  return bmp;
}

Question: Is there a native .NET approach that solves my particular problem? I'm trying to avoid using WinApi if there is a better way of doing it.

Community
  • 1
  • 1
Victor Zakharov
  • 25,801
  • 18
  • 85
  • 151
  • You say _"Graphics.CopyFromScreen assumes I want to copy with transparency key = Color.Black"_ I've never seen it do that...are you assuming it does that based on what you're seeing? Do you have a transparency key set on your form? Have you tried just saving that image to a file before you set it to your form background and seeing if it has the same artifact? – Chase Rocker Dec 24 '15 at 02:39
  • @ChaseRocker: Assumption based on what I'm seeing. I tried to save image to a file just now - it looks the same as on the above screenshot. I don't have any transparency related properties set - meaning they should all have default values. – Victor Zakharov Dec 24 '15 at 03:06
  • Are you certain that the transparent pixels are exactly Black? I'm testing your code and not seeing the issue. I'm running Win 7 with 32 bit color depth display. Have you tried passing the `CopyPixelOperation.SourceCopy` as a parameter into CopyFromScreen? – Chase Rocker Dec 24 '15 at 03:43
  • @ChaseRocker: You were right, they were *not exactly* black, it works with this color for example: RGB(13,11,12), also found (8,8,8). I tried to paint pure black in pbrush, it does not get whitened. `CopyPixelOperation.SourceCopy` does not make a difference. If I use PixelFormat.Format32bppRgb (without **A**), the *replacement* color is pure black, i.e. (0,0,0), instead of pure white (255,255,255). Also tried higher 48bit formats - same thing. – Victor Zakharov Dec 24 '15 at 13:52
  • Possible duplicate of [Capture screenshot Including Semitransparent windows in .NET](http://stackoverflow.com/questions/3072349/capture-screenshot-including-semitransparent-windows-in-net) – Victor Zakharov Dec 24 '15 at 14:03
  • @Neolisk I think the first thing you need to verify is whether the cause of the problem is the same as the one explained by Hans. To do that, remove the `CopyPixelOperation.CaptureBlt` from the `CreateBitmapFromDesktopNew`. If you get the same behavior as with using the `Graphics.CopyFromScreen`, I guess your are stuck with Hans solution (which IMO is not a big deal since the original method basically does the same - http://referencesource.microsoft.com/#System.Drawing/commonui/System/Drawing/Graphics.cs,f76c9e39776eeb24). – Ivan Stoev Dec 24 '15 at 16:01
  • @IvanStoev: Now it gets interesting, I removed `CopyPixelOperation.CaptureBlt` and the issue did not happen (opposite to what I expected). Meaning p/invoke call works better than the same thing via .NET framework. I guess it would be helpful to dig deeper into code to find a good explanation for such behavior, but maybe too much work for what it is really. – Victor Zakharov Dec 24 '15 at 18:40
  • 1
    To be honest, I had some suspicions. The difference could be from `CreateCompatibleDC/Bitmap` used in Hans solution. The documentation for [BitBlt function](https://msdn.microsoft.com/en-us/library/windows/desktop/dd183370(v=vs.85).aspx) contains the following remark "If the color formats of the source and destination device contexts do not match, the BitBlt function converts the source color format to match the destination format.". I don't know, looks like you should use p/invoke, I see no drawbacks. – Ivan Stoev Dec 24 '15 at 18:53
  • @IvanStoev: Thanks for looking into this. Ok, I'll use Hans' solution then. You could post as an answer to get some bonus points. :) – Victor Zakharov Dec 24 '15 at 19:02
  • I don't think I will, but thank you for the proposition! – Ivan Stoev Dec 24 '15 at 19:08

1 Answers1

1

Insert black background before to copy

g.Clear(System.Drawing.Color.Black);
g.CopyFromScreen(Point.Empty, Point.Empty, bounds.Size);
destcode
  • 11
  • 2