5

I need to capture screen shot without background picture (wallpaper). I can try to disable wallpaper, take screen shot and then enable it back, but:

  1. At moment i don't know how to disable/restore wallpaper (in simple case it is picture file assigned as desktop with some tiling options, but can it be in modern versions of Windows something different?).
  2. If user kills application before i switch wallpaper back, then wallpaper remain disabled and it is not good.

Does anyone know solution or ideas where to search for solution ? Maybe it is possible to disable wallpaper temporarily ?

Update: Screen shot is part of registering bug procedure, so i need all potentially usefull information (visible forms, taskbar, ...) and it is highly desired to keep screen shot in lossless format (more readable, faster compression). One of the options is capturing of shots to store them as AVI, so processing time is also important. Background makes images much larger, that is the only reason why i am trying to remove it. I can use some algorithms for decreasing of used colors, it highly improves compression ratio, but it is time consuming procedures. So best of all it would be to remove background picture at all.

Update 2: For generating of AVI from sequence of shots i use unit from François PIETTE (based on this article):

Avi := TAviFromBitmaps.CreateAviFile(
  nil,
  AviFilename,
  MKFOURCC('S', 'C', 'L', 'S'),     // msu-sc-codec
  2, 1);                            // 2 frames per second

// called by timer
procedure TfrmSnapshot.RecordFrame;
begin
  TakeSnapshot; // get snap shot to BMP:TBitmap
  Avi.AppendNewFrame(Bmp.Handle);
end;

So if i will able to delete background from snap shot, AVI compression will be improved also.

The part of final code i use:

  TAppRects = class
  protected
    FMonitor: TMonitor;
    FRects: TList<TRect>;

    function GetRegion(AArea: TRect): HRGN;
  public
    constructor Create(AMonitor: TMonitor);
    destructor Destroy; override;

    // fill all Area which is not covered by Rects (application forms)
    procedure FillBackground(ABmp: TBitmap; AArea: TRect);

    property Rects: TList<TRect> read FRects;
    property Monitor: TMonitor read FMonitor;
  end;

// Check for WS_EX_APPWINDOW will hide start button  menu/popup menus outside of
// the forms etc, but it makes final AVI much smaller (and usually it is anough
// to have main forms recorded).
function EnumWindowsProc(hwnd: HWND; lParam: LPARAM): BOOL; stdcall;
var
  r: TRect;
begin
  Result := True;
  if IsWindowVisible(hwnd) and
    (GetWindow(hwnd, GW_OWNER)=0) and // is not owned by another window
    (GetWindowLongPtr(hwnd, GWL_STYLE) and WS_EX_APPWINDOW<>0) and // is app
    GetWindowRect(hwnd, r) and
    (r.Width>0) and (r.Height>0)
  then
    with TAppRects(lParam) do
      if (FMonitor=nil) or
        (FMonitor.Handle=0) or
        (FMonitor.Handle=MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST))
      then
        FRects.Add(r);
end;

{ TAppRects }

constructor TAppRects.Create(AMonitor: TMonitor);
begin
  FMonitor := AMonitor;
  FRects := TList<TRect>.Create;
  EnumWindows(@EnumWindowsProc, NativeInt(self));
end;

destructor TAppRects.Destroy;
begin
  FreeAndNil(FRects);
end;

function TAppRects.GetRegion(AArea: TRect): HRGN;
var
  c: array of integer;
  p: array of TPoint;
  i: Integer;
begin
  setlength(c, FRects.Count);
  setlength(p, FRects.Count*4);
  for i := 0 to FRects.Count-1 do
  begin
    c[i] := 4;
    with FRects[i] do
    begin
      p[i*4  ] := Point(Left,Top);
      p[i*4+1] := Point(Right,Top);
      p[i*4+2] := Point(Right,Bottom);
      p[i*4+3] := Point(Left,Bottom);
    end;
  end;
  result := CreatePolyPolygonRgn(p[0], c[0], length(c), WINDING);
end;

procedure TAppRects.FillBackground(ABmp: TBitmap; AArea: TRect);
var
  h1,h2,h3: HRGN;
begin
  h1 := 0;
  h2 := 0;
  h3 := 0;
  try
    h1 := GetRegion(AArea);
    if h1=0 then
      exit;
    h2 := CreateRectRgn(AArea.Left,AArea.Top,AArea.Right,AArea.Bottom);
    h3 := CreateRectRgn(AArea.Left,AArea.Top,AArea.Right,AArea.Bottom);
    if (h2<>0) and (h3<>0) and
      not (CombineRgn(h3, h2,h1, RGN_DIFF) in [NULLREGION,RGN_ERROR])
    then
      FillRgn(ABmp.Canvas.Handle, h3, ABmp.Canvas.Brush.Handle);
  finally
    if h1<>0 then DeleteObject(h1);
    if h2<>0 then DeleteObject(h2);
    if h3<>0 then DeleteObject(h3);
  end;
end;

procedure RemoveBackground(ASnapshot: TBitmap; AMonitor: TMonitor);
var
  e: TAppRects;
  c: TColor;
begin
  e := nil;
  try
    e := TAppRects.Create(AMonitor);
    c := ASnapshot.Canvas.Brush.Color;
    ASnapshot.Canvas.Brush.Color := $FEA249; // kind of blue (~default for win8)
    e.FillBackground(ASnapshot, e.Monitor.WorkareaRect);
    ASnapshot.Canvas.Brush.Color := c;
  finally
    e.free;
  end;
end;
Andrei Galatyn
  • 3,322
  • 2
  • 24
  • 38
  • 3
    Would you mind backing up a step and describing why you need to exclude the wallpaper from the desktop? – Rob Kennedy Sep 30 '13 at 15:26
  • @Rob Just updated description. – Andrei Galatyn Sep 30 '13 at 16:11
  • Have you tried to save your screen shots in a 256 color compressed `PNG` format? Does the image size increases *significantly* when you include the wallpaper? `madExcept` uses such format by default and I'm quite happy with it. – kobik Sep 30 '13 at 16:35
  • @kobik Not sure how much it slows down compression, will check tomorrow. But anyway, one of option is save sequence of shots as AVI. I am trying to use MSU lossless codec for this. It works quite fast and provides good compression, but i have limited control on processing in this case and output is larger when there some background picture (my test is not 100% perfect, but it seems so). – Andrei Galatyn Sep 30 '13 at 16:46
  • Taking a screenshot of the user's entire desktop to submit a bug report? Maybe you're going about this the wrong way. I would go crazy mad if I found out an application I was using was taking screenshots of my entire desktop, background or not. Are you at least alerting the user and giving them a choice of whether they want to submit this screenshot or not? Not to mention, you want to save them as AVI, which tells me you're not just taking a screenshot, you're recording the user's screen and all their activity. – Jerry Dodge Oct 01 '13 at 00:54
  • But if this is really the case, and you really need to record the user's desktop, but only capture your own related things, then it seems to me that you might need to tap into the video drivers and get a real-time feed - something I've actually wanted to do but never got around to (I've been building a desktop surveillance / recording system) – Jerry Dodge Oct 01 '13 at 01:01
  • Note that if you want to have code for the avi. I need some source code from you that shows me how you're producing this AVI in the first place. – Johan Oct 01 '13 at 04:02

2 Answers2

4

Disabling the wallpaper is going to cause an annoying flicker/redraw. I expect, anyway. It would be cleaner to enumerate all of the windows on the desktop that are visible, find their dimensions/position, and then determine the area outside of all of those rectangles. Make that area invisible. i.e. white, to save paper when printing, or another color to suit your purpose. This answer is just to describe the general approach, but I think it's the way to go, unless some magic "silver bullet" appears.

Chris Thornton
  • 15,620
  • 5
  • 37
  • 62
  • 2
    But in this way all the icons on the desktop are also excluded. – Chen Sep 30 '13 at 15:25
  • 1
    What are you trying to capture a screen shot of? The active application window? – Bill Sep 30 '13 at 15:26
  • @Bill It is part of registering bug procedure, so i need all potentially usefull information (visible forms, taskbar, ...). But background makes image much larger. I think i can sacrifice icons from desktop, so suggestion from Chris seems to be interesting. If no one suggest something better, i will try to use this technique. – Andrei Galatyn Sep 30 '13 at 16:02
  • 2
    In this case, maybe you should go another step further and eliminate all windows that aren't from your app (that you're sending the debug/crash report for). Extraneous data is hazardous, when you consider the range of things that could be on the screen (password managers, XXX chat window, hit list, meth "super-lab" plans, etc.. – Chris Thornton Sep 30 '13 at 17:05
  • @Chris In my case it is not a problem and information from other apps (performance monitor, ProcessExplorer etc) can be usefull, it can be used users to show the problem. So at moment your idea to keep windows + task bar and remove everything else seems to be good option, thank you! I will check it tomorrow and then accept your answer. – Andrei Galatyn Sep 30 '13 at 20:47
  • Be ready to face possible lawsuits if you record the entire user's desktop. – Jerry Dodge Oct 01 '13 at 01:23
  • 1
    @Chris Just finished testing. It seems to be what i need. I also added some limitations for form to capture (only form of apps). This aproach has some side effect too of course, but in general it is ok. I will include part of the code in question to make it available to others. Thanks! – Andrei Galatyn Oct 01 '13 at 10:36
0

There is an easy way to do this:

  1. Take a copy of the current background picture and put this into a 2d array*.
  2. Take a screen shot and put it in a 2d array*.
  3. XOR the contents of both arrays.

*) or manipulate the bitmaps as if they're arrays. Make sure both bitmaps are the same color depth.

All pixels that are the same will show as black.

Now put the outcome of your manipulation into a mask; mask the screenshot, compress it with PNG and send.

Will produce code when I'm away from the iPad.

Johan
  • 74,508
  • 24
  • 191
  • 319
  • I think it can be done easier, it is same like if we make black every pixel of snap shot if it is equal to corresponding background pixel. But it has unplesant side effect - if some pixels/area of snapshot has same value as background, then they became black on snap shot. – Andrei Galatyn Oct 01 '13 at 06:13
  • The XOR is faster. There's a trick to getting rid of isolated pixels. You'll see. – Johan Oct 01 '13 at 06:48
  • 2
    I accepted answer from Chris, but if you have ready-to-show implementation of your idea it would be interesting to see :) I still have no clue how you going to avoid of "holes" in resulting snap shot in places where form has same color as background behind. – Andrei Galatyn Oct 01 '13 at 10:39