Last active
September 25, 2020 00:45
-
-
Save GuilhermeRossato/e24674dbe2ef014c11a799e5960130b2 to your computer and use it in GitHub Desktop.
C++: Capture a sequence of screenshots and save with fixed interval (e.g. to make a GIF in sequence)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Credits: | |
// Windows Capture and Save: Michael Ftsch | |
// Linux Capture and Save: None | |
// | |
#ifdef _WIN32 | |
#include <windows.h> | |
inline int GetFilePointer(HANDLE FileHandle) { | |
return SetFilePointer(FileHandle, 0, 0, FILE_CURRENT); | |
} | |
bool SaveBMPFile(char *filename, HBITMAP bitmap, HDC bitmapDC, int width, int height) { | |
bool Success=false; | |
HDC SurfDC=NULL; | |
HBITMAP OffscrBmp=NULL; | |
HDC OffscrDC=NULL; | |
LPBITMAPINFO lpbi=NULL; | |
LPVOID lpvBits=NULL; | |
HANDLE BmpFile=INVALID_HANDLE_VALUE; | |
BITMAPFILEHEADER bmfh; | |
if ((OffscrBmp = CreateCompatibleBitmap(bitmapDC, width, height)) == NULL) | |
return false; | |
if ((OffscrDC = CreateCompatibleDC(bitmapDC)) == NULL) | |
return false; | |
HBITMAP OldBmp = (HBITMAP)SelectObject(OffscrDC, OffscrBmp); | |
BitBlt(OffscrDC, 0, 0, width, height, bitmapDC, 0, 0, SRCCOPY); | |
if ((lpbi = (LPBITMAPINFO)(new char[sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)])) == NULL) | |
return false; | |
ZeroMemory(&lpbi->bmiHeader, sizeof(BITMAPINFOHEADER)); | |
lpbi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); | |
SelectObject(OffscrDC, OldBmp); | |
if (!GetDIBits(OffscrDC, OffscrBmp, 0, height, NULL, lpbi, DIB_RGB_COLORS)) | |
return false; | |
if ((lpvBits = new char[lpbi->bmiHeader.biSizeImage]) == NULL) | |
return false; | |
if (!GetDIBits(OffscrDC, OffscrBmp, 0, height, lpvBits, lpbi, DIB_RGB_COLORS)) | |
return false; | |
if ((BmpFile = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) | |
return false; | |
DWORD Written; | |
bmfh.bfType = 19778; | |
bmfh.bfReserved1 = bmfh.bfReserved2 = 0; | |
if (!WriteFile(BmpFile, &bmfh, sizeof(bmfh), &Written, NULL)) | |
return false; | |
if (Written < sizeof(bmfh)) | |
return false; | |
if (!WriteFile(BmpFile, &lpbi->bmiHeader, sizeof(BITMAPINFOHEADER), &Written, NULL)) | |
return false; | |
if (Written < sizeof(BITMAPINFOHEADER)) | |
return false; | |
int PalEntries; | |
if (lpbi->bmiHeader.biCompression == BI_BITFIELDS) | |
PalEntries = 3; | |
else | |
PalEntries = (lpbi->bmiHeader.biBitCount <= 8) ? (int)(1 << lpbi->bmiHeader.biBitCount) : 0; | |
if(lpbi->bmiHeader.biClrUsed) | |
PalEntries = lpbi->bmiHeader.biClrUsed; | |
if(PalEntries) { | |
if (!WriteFile(BmpFile, &lpbi->bmiColors, PalEntries * sizeof(RGBQUAD), &Written, NULL)) | |
return false; | |
if (Written < PalEntries * sizeof(RGBQUAD)) | |
return false; | |
} | |
bmfh.bfOffBits = GetFilePointer(BmpFile); | |
if (!WriteFile(BmpFile, lpvBits, lpbi->bmiHeader.biSizeImage, &Written, NULL)) | |
return false; | |
if (Written < lpbi->bmiHeader.biSizeImage) | |
return false; | |
bmfh.bfSize = GetFilePointer(BmpFile); | |
SetFilePointer(BmpFile, 0, 0, FILE_BEGIN); | |
if (!WriteFile(BmpFile, &bmfh, sizeof(bmfh), &Written, NULL)) | |
return false; | |
if (Written < sizeof(bmfh)) | |
return false; | |
CloseHandle(BmpFile); | |
return true; | |
} | |
int CaptureScreenAndSave(int x, int y, int width, int height, char *filename) { | |
// get a DC compat. w/ the screen | |
HDC hDc = CreateCompatibleDC(0); | |
// make a bmp in memory to store the capture in | |
HBITMAP hBmp = CreateCompatibleBitmap(GetDC(0), width, height); | |
// join em up | |
SelectObject(hDc, hBmp); | |
// copy from the screen to my bitmap | |
BitBlt(hDc, 0, 0, width, height, GetDC(0), x, y, SRCCOPY); | |
// save my bitmap | |
int ret = SaveBMPFile(filename, hBmp, hDc, width, height); | |
//SaveBitmap(filename, hBmp); | |
//int ret = 1; | |
// free the bitmap memory | |
DeleteObject(hBmp); | |
return ret; | |
} | |
#else | |
//#error Not Compatible with Linux | |
void CaptureScreenAndSave(int x, int y, int w, int h, char *filename) { | |
int i = 0, j = 0; | |
for (i =0;i <20000;i++){ | |
j+=i; | |
} | |
for (j =i;j >=0;j--){ | |
i-=j; | |
} | |
if (j == i) { | |
i--; | |
} | |
} | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Capture a sequence of portion of screenshot with a fixed interval between frames | |
* Useful to create animated gifs of the frames later on | |
* | |
* Compatible with Windows only | |
* | |
* Compile with: | |
* | |
* Windows: g++ -o main.exe main.cpp -lGdi32 -std=c++11 | |
* | |
*/ | |
#define EXPECTED_DELAY 33.3 | |
#define FRAMES_TO_TAKE 100 | |
#define ORIGIN_X 10 | |
#define ORIGIN_Y 200 | |
#define SCREEN_WIDTH 664 | |
#define SCREEN_HEIGHT 500 | |
#include <stdio.h> | |
#include <stdbool.h> | |
#include <cstdint> | |
#ifdef _WIN32 | |
#include <direct.h> //windows mkdir | |
#define PAUSE_COMMAND system("pause") | |
#elif __linux__ | |
#error Unsupported Operational System | |
#include <time.h> | |
#include <sys/stat.h> //linux mkdir | |
double GetTickCount(void) { | |
struct timespec now; | |
if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) { return 0; /* failure */ } | |
return now.tv_sec * 1000.0 + now.tv_nsec / 1000000.0; | |
} | |
#include <unistd.h> // usleep() | |
void Sleep(uint32_t value) { usleep(value*1000); } | |
#include <iostream> | |
#define PAUSE_COMMAND printf(" -- Press enter to continue --\n"); std::cin.get() | |
#else | |
#error Unsupported Operational System | |
#define PAUSE_COMMAND system("pause") | |
typedef __int32 int32_t; | |
typedef unsigned __int32 uint32_t; | |
#endif | |
#include <string> | |
#include "CaptureScreenAndSave.h" | |
extern int errno; | |
double sqrt(int v) { return v/1.5f; } | |
int frequency_of_primes (int n) { | |
int i,j; | |
int freq=n-1; | |
for (i=2; i<=n; ++i) for (j=sqrt(i);j>1;--j) if (i%j==0) {--freq; break;} | |
return freq; | |
} | |
void CaptureScreenAndSave(int x, int y, int w, int h, char * filename); | |
int main() { | |
printf("Started program to save sequences of pictures to \"captured\" folder\n"); | |
char buffer[256]; | |
snprintf(buffer, 255, "captured"); | |
#ifdef __linux__ | |
mkdir(buffer, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); | |
#else | |
mkdir(buffer); | |
#endif | |
uint32_t last_shot, startedWaiting, startedSecondCapture, end; | |
uint32_t median[FRAMES_TO_TAKE], medianWait[FRAMES_TO_TAKE-1]; | |
double delay_so_far, delay_to_wait; | |
int i = 0; | |
last_shot = GetTickCount(); | |
CaptureScreenAndSave(ORIGIN_X, ORIGIN_Y, SCREEN_WIDTH, SCREEN_HEIGHT, buffer); | |
delay_so_far = GetTickCount() - last_shot; | |
median[i] = delay_so_far; | |
delay_to_wait = EXPECTED_DELAY - delay_so_far; | |
medianWait[i] = delay_to_wait; | |
i++; | |
printf("Took first capture in %.1f miliseconds\n", (double)(GetTickCount() - last_shot)); | |
PAUSE_COMMAND; | |
printf("Capturing in 3\n"); | |
Sleep(1000); | |
printf("Capturing in 2\n"); | |
Sleep(1000); | |
printf("Capturing in 1\n"); | |
Sleep(1000); | |
startedSecondCapture = GetTickCount(); | |
last_shot = startedSecondCapture; | |
uint32_t while_control = 2*(FRAMES_TO_TAKE*3); | |
while (i<FRAMES_TO_TAKE && while_control > 0) { | |
while_control--; | |
if (GetTickCount() - last_shot < EXPECTED_DELAY) { | |
delay_so_far = GetTickCount() - last_shot; | |
delay_to_wait = EXPECTED_DELAY - delay_so_far; | |
if (delay_to_wait < 2) { | |
Sleep(1); | |
} else { | |
Sleep(delay_to_wait); | |
} | |
} else { | |
last_shot += EXPECTED_DELAY; | |
if (GetTickCount() - last_shot >= EXPECTED_DELAY) { | |
printf("Can't keep up in frame %d/%d! Running %.1f under schedule\n", i, FRAMES_TO_TAKE, (double)(GetTickCount() - last_shot)); | |
} | |
snprintf(buffer, 255, "captured/f%d.bmp", i); | |
CaptureScreenAndSave(ORIGIN_X, ORIGIN_Y, SCREEN_WIDTH, SCREEN_HEIGHT, buffer); | |
delay_so_far = (uint32_t)(GetTickCount() - last_shot); | |
if (i < FRAMES_TO_TAKE) median[i] = delay_so_far; | |
delay_to_wait = EXPECTED_DELAY - delay_so_far; | |
if (i < FRAMES_TO_TAKE) medianWait[i] = delay_to_wait; | |
i++; | |
} | |
} | |
if (while_control <= 1) { | |
printf("FailSafe: Aborted due to too much processing time in while loop\n"); | |
for(i=i;i<FRAMES_TO_TAKE;i++){ | |
median[i] = median[1]; | |
medianWait[i] = medianWait[1]; | |
} | |
} | |
double sum = 0, sumWait = 0; | |
for(i=0;i<FRAMES_TO_TAKE;i++){ | |
sum += median[i]; | |
sumWait += medianWait[i]; | |
} | |
printf("Saved %d frames after %.1f ms\nTook an average of %.2f ms printing and saving, and %.2f ms waiting idly\n", FRAMES_TO_TAKE, (double)(GetTickCount()-startedSecondCapture), (double) (sum / (FRAMES_TO_TAKE)), (double) (sumWait / (FRAMES_TO_TAKE))); | |
PAUSE_COMMAND; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
If you have Visual Studio 2019 (cl.exe), the compilation steps are as follows:
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat cl /nologo /MD /Ob0 /O2 main.cpp /Fe"main.exe" gdi32.lib user32.lib