zoukankan      html  css  js  c++  java
  • 用delphi实现完美屏幕截图

    可以截取layered窗口(包括透明窗口)的代码:

    procedure CaptureScreen(AFileName: string);
    const
      CAPTUREBLT = $40000000;
    var
      hdcScreen: HDC;
      hdcCompatible: HDC;
      bmp: TBitmap;
      hbmScreen: HBITMAP;
    begin
      hdcScreen := CreateDC('DISPLAY', nil, nil, nil);
      hdcCompatible := CreateCompatibleDC(hdcScreen);
      hbmScreen := CreateCompatibleBitmap(hdcScreen,
        GetDeviceCaps(hdcScreen, HORZRES),
        GetDeviceCaps(hdcScreen, VERTRES));
      SelectObject(hdcCompatible, hbmScreen);
      bmp := TBitmap.Create;
      bmp.Handle := hbmScreen;
      BitBlt(hdcCompatible,
        0, 0,
        bmp.Width, bmp.Height,
        hdcScreen,
        0, 0,
        SRCCOPY or CAPTUREBLT);

      bmp.SaveToFile(AFileName);
      bmp.Free;
      DeleteDC(hdcScreen);
      DeleteDC(hdcCompatible);
    end;

    DX Primary Surface截图代码!包含DX8与DX9两个版本

    ...
    interface

    {$DEFINE D3D9}

    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, Buttons,
    {$IFDEF D3D9}
      // D3DX9, // use D3D to save surface
      Direct3D9
    {$ELSE}
      // D3DX8, // use D3D to save surface
      Direct3D8
    {$ENDIF};
    ...
    procedure TForm1.BitBtn1Click(Sender: TObject);
    // Capture screen through D3D.
    var
      BitsPerPixel: Byte;
      {$IFDEF D3D9}
      pD3D: IDirect3D9;
      pSurface: IDirect3DSurface9;
      g_pD3DDevice: IDirect3DDevice9;
      {$ELSE}
      pD3D: IDirect3D8;
      pSurface: IDirect3DSurface8;
      g_pD3DDevice: IDirect3DDevice8;
      {$ENDIF}
      D3DPP: TD3DPresentParameters;
      ARect: TRect;
      LockedRect: TD3DLockedRect;
      BMP: TBitmap;
      i, p: Integer;
    begin
      BitsPerPixel := GetDeviceCaps(Canvas.Handle, BITSPIXEL);
      FillChar(d3dpp, SizeOf(d3dpp), 0);
      D3DPP.Windowed := True;
      D3DPP.Flags := D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
      D3DPP.SwapEffect := D3DSWAPEFFECT_DISCARD;
      D3DPP.BackBufferWidth := Screen.Width;
      D3DPP.BackBufferHeight := Screen.Height;
      D3DPP.BackBufferFormat := D3DFMT_X8R8G8B8;
      {$IFDEF D3D9}
      pD3D := Direct3DCreate9(D3D_SDK_VERSION);
      pD3D.CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, GetDesktopWindow,
        D3DCREATE_SOFTWARE_VERTEXPROCESSING, @D3DPP, g_pD3DDevice);
      g_pD3DDevice.CreateOffscreenPlainSurface(Screen.Width, Screen.Height, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, pSurface, nil);
      g_pD3DDevice.GetFrontBufferData(0, pSurface);
      {$ELSE}
      pD3D := Direct3DCreate8(D3D_SDK_VERSION);
      pD3D.CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, GetDesktopWindow,
        D3DCREATE_SOFTWARE_VERTEXPROCESSING, D3DPP, g_pD3DDevice);
      g_pD3DDevice.CreateImageSurface(Screen.Width, Screen.Height, D3DFMT_A8R8G8B8, pSurface);
      g_pD3DDevice.GetFrontBuffer(pSurface);
      {$ENDIF}
      // use D3D to save surface. Notes: D3DX%ab.dll is required!
    //  D3DXSaveSurfaceToFile('Desktop.bmp', D3DXIFF_BMP, pSurface, nil,  nil);
      // use Bitmap to save surface
      ARect := Screen.DesktopRect;
      pSurface.LockRect(LockedRect, @ARect, D3DLOCK_NO_DIRTY_UPDATE or D3DLOCK_NOSYSLOCK or D3DLOCK_READONLY);
      BMP := TBitmap.Create;
      BMP.Width := Screen.Width;
      BMP.Height := Screen.Height;
      case BitsPerPixel of
        8:  BMP.PixelFormat := pf8bit;
        16: BMP.PixelFormat := pf16bit;
        24: BMP.PixelFormat := pf24bit;
        32: BMP.PixelFormat := pf32bit;
      end;
      p := Cardinal(LockedRect.pBits);
      for i := 0 to Screen.Height - 1 do
        begin
          CopyMemory(BMP.ScanLine[i], Ptr(p), Screen.Width * BitsPerPixel div 8);
          p := p + LockedRect.Pitch;
        end;
      BMP.SaveToFile('Desktop.bmp');
      BMP.Free;
      pSurface.UnlockRect;
    end;

    以上DX截图代码,不需要额外的DLL支持,有DirectX 9.0即可

    采用上面的2个方案以外,还有些视频播放器的图像不能截取吧,呵呵

    怎么解决呢?

    它们使用的,是称为"覆盖表面"的技术,截取覆盖表面,需要Hook的手段才行

    思路是:
    通过Hook DDraw的DirectDrawCreate(RealOne用)同DirectDrawCreateEx(WMP用)
    获得IDirectDraw(7)
    再COM Hook CreateSurface,注意RealOne使用的是通过QueryInterface获得IDirectDraw2
    WMP则是IDirectDraw7

    Hook了CreateSurface后,就能获得OverlaySurface

    所以必须在软件使用前,启动全局Hook,才有效


    在需要截图的时候
    Lock Overlay Surface,读取数据,马上Unlock,以免损失性能

    解码读出来的数据,即可,但是由于获得的数据是显卡硬件VRAM的数据,一般是YUY2,YV12等格式,需要转换为RGB格式

    例如,在我的GF6600上,RealOne(RMVB)用的是YUY2,而WMP(AVI)用的是YV12,还与当前播放的文件格式有关

    提供主表面截图源码和覆盖表面截图的测试程序http://lysoft.lz169.com/projects/DXCapture.rar
    现在支持YV12,NV12,YUY2,UUVY 4个格式

  • 相关阅读:
    《软件需求十步走》阅读笔记一
    专业实训项目需求分析
    2015秋季学期个人阅读计划
    最后期限阅读笔记3
    最后期限阅读笔记2
    最后期限阅读笔记1
    你的灯亮着么阅读笔记3
    DPDK(二):准备知识7---linux虚拟网络设备
    DPDK(二)准备6---PMD
    DPDK(二):准备知识4 --- 无锁FIFO环形队列
  • 原文地址:https://www.cnblogs.com/MaxWoods/p/3142403.html
Copyright © 2011-2022 走看看