SRL Archive - General

takeScreen shot


rj

2014-01-18 16:53:31

I was bored and messing around with simba last night, and I wrote up a little take screenshot function..


procedure takeScreenshot(x1, y1, x2, y2:integer);
var
i, k, c:integer;
begin
setLength(tpa, (x2 - x1) * (y2 - y1));
setLength(colors, length(tpa));
for i := x1 to x2 - 1 do
for k := y1 to y2 - 1 do
begin
tpa[c] := point(i, k);
Colors[c] := getColor(i, k);
c := c + 1;
end;
end;

[spoiler=full]

var
tpa:tpointarray;
colors:tintegerarray;



procedure showScreenShot(x1, y1, x2, y2:integer);
var
tpa2:tpointarray;
bmp, i:integer;
begin
setLength(tpa2, length(tpa));
for i := 0 to high(tpa) do
tpa2[i] := point(tpa[i].x - x1, tpa[i].y - y1);
bmp := createBitmap(x2 - x1, y2 - y1);
fastsetpixels(bmp, tpa2, colors);
DisplayDebugImgWindow(x2 - x1 + 1, y2 - y1 + 1);
DrawBitmapDebugImg(Bmp);
freebitmap(bmp);
end;

procedure takeScreenshot(x1, y1, x2, y2:integer);
var
i, k, c:integer;
begin
setLength(tpa, (x2 - x1) * (y2 - y1));
setLength(colors, length(tpa));
for i := x1 to x2 - 1 do
for k := y1 to y2 - 1 do
begin
tpa[c] := point(i, k);
Colors[c] := getColor(i, k);
c := c + 1;
end;
showScreenShot(x1, y1, x2, y2);
end;

var
t:integer;

begin
t := getsystemTime;
takeScreenshot(2, 2, 843, 857);
writeln('Took ' , GetsystemTime - t, ' ms');
end.
[/spoiler]

It's pretty darn slow, (4000 ms for my like 1900X1700 screen or something) I didn't really do it for speed or any purpose, I just wanted to make the function on my own without the help of anything (sort of)



But I was wondering, how could I replicate this in c++? How would I actually capture the window and then display the image?

my c++ code so far:

#include <iostream>
#include<windows.h>

using namespace std;



void takeScreenshot(int x1, int y1, int x2, int y2)
{
std::cout << "Hello World!";
}
int main()
{
takeScreenshot(0, 0, 5, 5)
return 0;
}

lol

Brandon

2014-01-18 17:18:44


#include <windows.h>
#include <iostream>
#include <fstream>
#include <chrono>
#include <ctime>

typedef struct
{
HBITMAP hBmp;
int Width, Height;
unsigned int* Pixels;
unsigned short BitsPerPixel;
} BmpData;

bool ScreenShot(BmpData &Data, HDC DC)
{
bool Result = false;
memset(&Data, 0, sizeof(Data));
HDC hdcMem = CreateCompatibleDC(DC);
Data.Width = GetDeviceCaps(DC, HORZRES);
Data.Height = GetDeviceCaps(DC, VERTRES);

unsigned char* Pixels = NULL;
unsigned short BitsPerPixel = 32;
BITMAPINFO Info = {{sizeof(BITMAPINFOHEADER), Data.Width, -Data.Height, 1, BitsPerPixel, BI_RGB, 0, 0, 0, 0, 0}};

Data.hBmp = CreateDIBSection(DC, &Info, DIB_RGB_COLORS, reinterpret_cast<void**>(&Pixels), NULL, 0);
if(Data.hBmp)
{
HBITMAP hOld = reinterpret_cast<HBITMAP>(SelectObject(hdcMem, Data.hBmp));
BitBlt(hdcMem, 0, 0, Data.Width, Data.Height, DC, 0, 0, SRCCOPY);
SelectObject(hdcMem, hOld);
Data.Height *= -1;
Data.BitsPerPixel = BitsPerPixel;
Data.Pixels = reinterpret_cast<unsigned int*>(Pixels);
Result = true;
}

DeleteDC(hdcMem);
return Result;
}

/*Portable way to save a bitmap =)*/
void SaveBitmap(const char* FilePath, const BmpData &Data)
{
std::fstream hFile(FilePath, std::ios::out | std::ios::binary);
if (!hFile.is_open())
{
printf("%s", "Error. Cannot Create Bitmap.");
return;
}

unsigned int Trash = 0;
unsigned short Planes = 1;
unsigned int biSize = 40;
unsigned short Type = 0x4D42;
unsigned int compression = 0;
unsigned int PixelsOffsetBits = 54;
int Width = Data.Width;
int Height = -Data.Height;
unsigned short BitsPerPixel = Data.BitsPerPixel;
unsigned int size = ((Width * BitsPerPixel + 31) / 32) * 4 * Height;
unsigned int bfSize = 54 + size;
Height *= -1;

hFile.write(reinterpret_cast<char*>(&Type), sizeof(Type));
hFile.write(reinterpret_cast<char*>(&bfSize), sizeof(bfSize));
hFile.write(reinterpret_cast<char*>(&Trash), sizeof(unsigned int));
hFile.write(reinterpret_cast<char*>(&PixelsOffsetBits), sizeof(PixelsOffsetBits));
hFile.write(reinterpret_cast<char*>(&biSize), sizeof(biSize));
hFile.write(reinterpret_cast<char*>(&Width), sizeof(Width));
hFile.write(reinterpret_cast<char*>(&Height), sizeof(Height));
hFile.write(reinterpret_cast<char*>(&Planes), sizeof(Planes));
hFile.write(reinterpret_cast<char*>(&BitsPerPixel), sizeof(BitsPerPixel));
hFile.write(reinterpret_cast<char*>(&compression), sizeof(compression));
hFile.write(reinterpret_cast<char*>(&size), sizeof(size));
hFile.write(reinterpret_cast<char*>(&Trash), sizeof(unsigned int));
hFile.write(reinterpret_cast<char*>(&Trash), sizeof(unsigned int));
hFile.write(reinterpret_cast<char*>(&Trash), sizeof(unsigned int));
hFile.write(reinterpret_cast<char*>(&Trash), sizeof(unsigned int));
hFile.write(reinterpret_cast<char*>(Data.Pixels), size);
hFile.close();
}

std::chrono::time_point<std::chrono::high_resolution_clock> SystemTime()
{
return std::chrono::high_resolution_clock::now();
}

float FloatTimeDuration(std::chrono::time_point<std::chrono::high_resolution_clock> Time)
{
return std::chrono::duration<float>(SystemTime() - Time).count() * 1000.0f;
}

unsigned int TimeDuration(std::chrono::time_point<std::chrono::high_resolution_clock> Time)
{
return std::chrono::duration_cast<std::chrono::milliseconds>(SystemTime() - Time).count();
}

int main()
{
HWND MyWindow = nullptr; //Handle to the window that you want to screenshot..

BmpData data;
HDC Screen = GetDC(MyWindow); //Get a DC..

auto Time = SystemTime();
ScreenShot(data, Screen);
std::cout<<"It took: "<<FloatTimeDuration(Time)<<"ms to take a screenshot!\n";

SaveBitmap("C:/Users/Brandon/desktop/Foo.bmp", data);
DeleteDC(Screen);
DeleteObject(data.hBmp);
}

rj

2014-01-18 17:20:08

@Brandon; C:\Users\\Desktop\Takescreenshot\main.cpp|86|error: 'nullptr' was not declared in this scope|


hmm am I supposed to input a windowname?

Flight

2014-01-18 17:21:58

I'm guessing Warpie can help speed this up.

Brandon

2014-01-18 17:27:10

From: Officer Barbrady
@Brandon; C:\Users\\Desktop\Takescreenshot\main.cpp|86|error: 'nullptr' was not declared in this scope|


hmm am I supposed to input a windowname?



You need to enable C++11 to use nullptr.

http://i.imgur.com/d0V3BFk.png


Secondly.. You're supposed to replace the nullptr with GetDesktopWindow(); or whatever the handle to the window is that you want to screenshot.

Finally, to optimise your Simba code:


var
t:integer;

begin
t := getsystemTime;
FreeBitmap(BitmapFromClient(2, 2, 843, 857)); //Takes a screenshot and deletes it.
writeln('Took ' , GetsystemTime - t, ' ms');
end.

Olly

2014-01-18 17:28:59

Uh, Are you aware there is SaveScreenshot in Simba?

Also yikes, I couldn't think of a way to make it slower…!

Turpinator

2014-01-18 17:37:32

From: Olly
Uh, Are you aware there is SaveScreenshot in Simba?

Also yikes, I couldn't think of a way to make it slower…!


Oh i could. make it save each color in rgb in 3 separate arrays!

slacky

2014-01-19 03:31:10

From: Turpinator
Oh i could. make it save each color in rgb in 3 separate arrays!

Why would you do that? Also, you have TMufasaBitmap.GetData() which returns an record array (R,G,B,A), no need for separate arrays…
The color-int you get normally can easly "be bitshifted" (which is very fast) so you can get the R,G,B values that way.

I would avoid doing anything related to a lot of iterations within a (slow) interpreted language like PS, even Lape would be quite a bit to slow.


Fastest way to grab a screenshot in Simba is (as far as I remember):
> BitmapFromClient(x1, y1, x2, y2)
To get the whole screen you would need to set the client as the mainscreen (pretty sure you can do that within the function).

If you want the data separated (like in the first post) you can do something like:

procedure UglyScreenShotThingyCrap(x1,y1,x2,y2: Integer;
var TPA:TPointArray; var Colors:TIntegerArray);
var
im: Integer;
begin
im := BitmapFromClient(0,0,x2,y2);
TPA := TPAFromBox(IntToBox(x1,y1,x2,y2));
Colors := FastGetPixels(im,TPA);
FreeBitmap(im);
end;

var
T: Integer;
TPA:TPointArray;
Colors:TIntegerArray;
begin
t := GetTimeRunning;
UglyScreenShotThingyCrap(0,0,850,850, TPA, Colors);
writeln('Took ', GetTimeRunning - t, ' ms');
end.


If you just want direct array access to the data I would prefer a matrix.. And thanks to Olly we have BitmapToMatrix in Simba:

...
var
im: Integer;
Matrix: T2DIntegerArray;
begin
im := BitmapFromClient(0,0,800,600); //Screenshot.
Matrix := BitmapToMatrix(im);

WriteLn(Matrix[300][300]); //Index with Matrix[y][x]
end;