5

我需要捕获没有背景图片(壁纸)的屏幕截图。我可以尝试禁用壁纸,截屏然后重新启用它,但是:

  1. 目前我不知道如何禁用/恢复壁纸(在简单的情况下,它是分配为带有一些平铺选项的桌面的图片文件,但它可以在现代版本的 Windows 中有所不同吗?)。
  2. 如果用户在我切换回壁纸之前杀死了应用程序,那么壁纸将保持禁用状态,这并不好。

有谁知道解决方案或想法在哪里寻找解决方案?也许可以暂时禁用壁纸?

更新:屏幕截图是注册错误程序的一部分,因此我需要所有可能有用的信息(可见表单、任务栏等),并且非常希望将屏幕截图保持为无损格式(更具可读性,更快的压缩)。其中一种选择是捕捉镜头以将它们存储为 AVI,因此处理时间也很重要。背景使图像更大,这是我试图删除它的唯一原因。我可以使用一些算法来减少使用的颜色,它极大地提高了压缩率,但这是一个耗时的过程。所以最好的办法是完全删除背景图片。

更新 2:为了从镜头序列中生成 AVI,我使用了 François PIETTE 的单位(基于这篇文章):

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;

因此,如果我能够从快照中删除背景,AVI 压缩也会得到改善。

我使用的最终代码部分:

  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;
4

2 回答 2

4

禁用墙纸会导致烦人的闪烁/重绘。无论如何,我期待。枚举桌面上所有可见的窗口,找到它们的尺寸/位置,然后确定所有这些矩形之外的区域会更干净。使该区域不可见。即白色,打印时节省纸张,或其他颜色以适合您的目的。这个答案只是为了描述一般方法,但我认为这是要走的路,除非出现一些神奇的“银弹”。

于 2013-09-30T15:22:14.993 回答
0

有一种简单的方法可以做到这一点:

  1. 获取当前背景图片的副本并将其放入二维数组*。
  2. 截取屏幕截图并将其放入二维数组*。
  3. 异或两个数组的内容。

*) 或像处理数组一样操作位图。确保两个位图的颜色深度相同。

所有相同的像素都将显示为黑色。

现在将您的操作结果放入掩码中;屏蔽屏幕截图,用 PNG 压缩并发送。

当我离开 iPad 时会生成代码。

于 2013-10-01T03:56:59.920 回答