• Uncategorized

About c : Take-a-screenshot-in-C—windows

Question Detail

I want to take a screenshot in C on my windows machine and save it as a jpg or bmp or whatever. Anyway, I tried to do it by my own, it’s okay and working well but it’s UNBEARABLY SLOW, unlike prt scr key – I wonder if there’s a way to access prt scr clipboard and somehow paste it in a jpg/png file OR if there’s a faster way to get all of the screen pixels.
That’s my code:

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

The function save_png_to_file is working propely, the loop is taking too long (my screen is 1366×768 and it’s over million loop entries) – why is it so slow when the key prt scr is doing it easily?

Question Answer

Hmm… GetPixel() is really slow by itself, then combining it in a loop with this many do-overs… Can’t be good.
The function BitBlt is A LOT faster – here is an example:

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

This is explained here:

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

See the CaptureAnImage() function in the example. The whole code is a bit complex because it is a full win32 app but it shows how to capture a sepcific window and save to filename.bmp.

You may also like...

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.