【问题标题】:Take a screenshot in C - windows在 C - windows 中截取屏幕截图
【发布时间】:2014-08-08 09:20:25
【问题描述】:

我想在我的 Windows 机器上用 C 语言截取屏幕截图并将其保存为 jpg 或 bmp 或其他格式。无论如何,我尝试自己做,没关系,运行良好,但速度慢得让人难以忍受,不像prt scr 键 - 我想知道是否有办法访问prt scr 剪贴板并以某种方式将其粘贴到 jpg/png 文件中或如果有更快的方法来获取所有屏幕像素。 这是我的代码:

int main()
{
    bitmap_t pic;
    int i, j;
    pic.width = GetSystemMetrics(SM_CXSCREEN);
    pic.height = GetSystemMetrics(SM_CYSCREEN);

    pic.pixels = (pixel_t**)malloc(sizeof(pixel_t*)*pic.width);
    for(i = 0 ; i < pic.height ; i++)
    {
        pic.pixels[i] = (pixel_t*)malloc(sizeof(pixel_t)*pic.height);
    }

    HDC hdc = GetDC(NULL);
    COLORREF c;
    printf("Size of your monitor is %d by %d.\n", pic.width, pic.height);
    for(i = 0 ; i < pic.width ; i++)
    {
        for(j = 0 ; j < pic.height ; j++)
        {
            c = GetPixel(hdc, i, j);
            pic.pixels[i][j].red = (uint8_t)GetRValue(c);
            pic.pixels[i][j].green = (uint8_t)GetGValue(c);
            pic.pixels[i][j].blue = (uint8_t)GetBValue(c);
        }
    }

    ReleaseDC(NULL, hdc);
    save_png_to_file(&pic, "D:\\pic.png");

    for(i = 0 ; i < pic.height ; i++)
    {
        free(pic.pixels[i]);
    }
    free(pic.pixels);

    return 0;
}

save_png_to_file 函数工作正常,循环时间太长(我的屏幕是 1366x768,循环条目数超过百万) - 为什么键 prt scr 轻松完成时它这么慢?

【问题讨论】:

  • 喜欢this?
  • 您可能已经找到了最慢的方法。 :) 这个question 有点细节,但建议是正确的,你想创建一个位图并将屏幕blit 到其中,然后对该位图进行操作。 GetPixel 很慢,但如果你是从显存中复制,那就更糟了。
  • @RetiredNinja 我看到有 BitBlt,我了解它的用法 - 但是如何将其保存为 png/jpeg ?甚至作为 bmp
  • 你使用的是什么库?

标签: c windows screenshot


【解决方案1】:

嗯...GetPixel() 本身真的很慢,然后将它与这么多的重做组合成一个循环...不能很好。 函数 BitBlt 快了很多 - 这是一个例子:

BOOL SaveToFile(HBITMAP hBitmap3, LPCTSTR lpszFileName)
{   
  HDC hDC;
  int iBits;
  WORD wBitCount;
  DWORD dwPaletteSize=0, dwBmBitsSize=0, dwDIBSize=0, dwWritten=0;
  BITMAP Bitmap0;
  BITMAPFILEHEADER bmfHdr;
  BITMAPINFOHEADER bi;
  LPBITMAPINFOHEADER lpbi;
  HANDLE fh, hDib, hPal,hOldPal2=NULL;
  hDC = CreateDC("DISPLAY", NULL, NULL, NULL);
  iBits = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES);
  DeleteDC(hDC);
  if (iBits <= 1)
    wBitCount = 1;
  else if (iBits <= 4)
    wBitCount = 4;
  else if (iBits <= 8)
    wBitCount = 8;
  else
    wBitCount = 24; 
  GetObject(hBitmap3, sizeof(Bitmap0), (LPSTR)&Bitmap0);
  bi.biSize = sizeof(BITMAPINFOHEADER);
  bi.biWidth = Bitmap0.bmWidth;
  bi.biHeight =-Bitmap0.bmHeight;
  bi.biPlanes = 1;
  bi.biBitCount = wBitCount;
  bi.biCompression = BI_RGB;
  bi.biSizeImage = 0;
  bi.biXPelsPerMeter = 0;
  bi.biYPelsPerMeter = 0;
  bi.biClrImportant = 0;
  bi.biClrUsed = 256;
  dwBmBitsSize = ((Bitmap0.bmWidth * wBitCount +31) & ~31) /8
                                                * Bitmap0.bmHeight; 
  hDib = GlobalAlloc(GHND,dwBmBitsSize + dwPaletteSize + sizeof(BITMAPINFOHEADER));
  lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);
  *lpbi = bi;

  hPal = GetStockObject(DEFAULT_PALETTE);
  if (hPal)
  { 
    hDC = GetDC(NULL);
    hOldPal2 = SelectPalette(hDC, (HPALETTE)hPal, FALSE);
    RealizePalette(hDC);
  }


  GetDIBits(hDC, hBitmap3, 0, (UINT) Bitmap0.bmHeight, (LPSTR)lpbi + sizeof(BITMAPINFOHEADER) 
    +dwPaletteSize, (BITMAPINFO *)lpbi, DIB_RGB_COLORS);

  if (hOldPal2)
  {
    SelectPalette(hDC, (HPALETTE)hOldPal2, TRUE);
    RealizePalette(hDC);
    ReleaseDC(NULL, hDC);
  }

  fh = CreateFile(lpszFileName, GENERIC_WRITE,0, NULL, CREATE_ALWAYS, 
    FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); 

  if (fh == INVALID_HANDLE_VALUE)
    return FALSE; 

  bmfHdr.bfType = 0x4D42; // "BM"
  dwDIBSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwPaletteSize + dwBmBitsSize;
  bmfHdr.bfSize = dwDIBSize;
  bmfHdr.bfReserved1 = 0;
  bmfHdr.bfReserved2 = 0;
  bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER) + dwPaletteSize;

  WriteFile(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);

  WriteFile(fh, (LPSTR)lpbi, dwDIBSize, &dwWritten, NULL);
  GlobalUnlock(hDib);
  GlobalFree(hDib);
  CloseHandle(fh);

  return TRUE;
} 

int screenCapture(int x, int y, int w, int h, LPCSTR fname)
{
    HDC hdcSource = GetDC(NULL);
    HDC hdcMemory = CreateCompatibleDC(hdcSource);

    int capX = GetDeviceCaps(hdcSource, HORZRES);
    int capY = GetDeviceCaps(hdcSource, VERTRES);

    HBITMAP hBitmap = CreateCompatibleBitmap(hdcSource, w, h);
    HBITMAP hBitmapOld = (HBITMAP)SelectObject(hdcMemory, hBitmap);

    BitBlt(hdcMemory, 0, 0, w, h, hdcSource, x, y, SRCCOPY);
    hBitmap = (HBITMAP)SelectObject(hdcMemory, hBitmapOld);

    DeleteDC(hdcSource);
    DeleteDC(hdcMemory);

    HPALETTE hpal = NULL;
    if(SaveToFile(hBitmap, fname)) return 1;
    return 0;
}

int main()
{
    screenCapture(0, 0, GetSystemMetric, 768, "D:\\MyFirstScreeshot.bmp");

    return 0;
}

【讨论】:

  • 这该死的烂摊子,真的有必要截屏吗?
  • 我在 Visual Studio 中对此进行了测试,但没有成功。必须修复几个问题才能编译。必须通过执行 LPCTSTR(fname) ... 来强制转换 fname 并且必须将参数添加到第三个参数 GetSystemMetrics(SM_CXSCREEN)
  • 要使其可编译,请在顶部添加#include&lt;windows.h&gt;,将GetSystemMetric 替换为GetSystemMetrics(0),并添加额外的库为user32.libgdi32.lib
  • 我使用tcc -impdef User32.dll &amp; tcc -impdef Gdi32.dll &amp; tcc -lGdi32 -lUser32 screenshot.c用tcc编译它
  • 我建议将 GetSystemMetric 替换为 1280
【解决方案2】:

这里有解释:

http://msdn.microsoft.com/en-us/library/dd183402(v=vs.85).aspx

参见示例中的CaptureAnImage() 函数。整个代码有点复杂,因为它是一个完整的 win32 应用程序,但它显示了如何捕获特定窗口并保存到 filename.bmp

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-20
    • 1970-01-01
    • 1970-01-01
    • 2014-09-26
    • 1970-01-01
    相关资源
    最近更新 更多