Skip to content

Commit a591ba3

Browse files
committed
Added bitmap converter
1 parent b97c1c4 commit a591ba3

10 files changed

+167
-35
lines changed

CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ add_executable(LiveStreamClient
1111
includes/api/WebSocketConnection.h
1212
includes/api/Server.h
1313
includes/BrainsBehindScreenshot.h
14+
includes/BitmapConverter.h
1415
src/BrainsBehindScreenshot.cpp
16+
src/BitmapConverter.cpp
1517
src/api/server.cpp
1618
src/api/WebSocketClient.cpp
1719
src/api/WebSocketConnection.cpp

includes/BitmapConverter.h

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//
2+
// Created by marco on 16/11/2024.
3+
//
4+
5+
#ifndef BITMAPCONVERTER_H
6+
#define BITMAPCONVERTER_H
7+
8+
#include <windows.h>
9+
#include <vector>
10+
#include <string>
11+
12+
class BitmapConverter {
13+
public:
14+
static std::vector<BYTE> HBitmapToRGB(HBITMAP hBitmap, int width, int height);
15+
16+
static std::vector<BYTE> ResizeImage(const std::vector<BYTE> &pixels, int originalWidth, int originalHeight,
17+
int newWidth, int newHeight);
18+
19+
static std::vector<int> CategorizePixels(const std::vector<BYTE> &pixels, int width, int height);
20+
21+
static std::string PixelsToString(const std::vector<int> &pixels);
22+
};
23+
24+
#endif //BITMAPCONVERTER_H

includes/BrainsBehindScreenshot.h

+9-4
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,22 @@
1414
class ScreenCapture {
1515
public:
1616
ScreenCapture();
17+
1718
~ScreenCapture();
1819

19-
static bool CaptureScreen(const std::wstring& filename);
20-
static BITMAP CaptureScreenBitmap();
20+
static bool CaptureScreen(const std::wstring &filename);
21+
22+
static HBITMAP CaptureScreenBitmap(int &width, int &height);
2123

2224
private:
2325
ULONG_PTR gdiplusToken{};
2426

2527
void InitializeGDIPlus();
28+
2629
void ShutdownGDIPlus() const;
27-
static void SaveBitmapToFile(HBITMAP hBitmap, const std::wstring& filename);
28-
static int GetEncoderClsid(const WCHAR* format, CLSID* pClsid);
30+
31+
static void SaveBitmapToFile(HBITMAP hBitmap, const std::wstring &filename);
32+
33+
static int GetEncoderClsid(const WCHAR *format, CLSID *pClsid);
2934
};
3035
#endif //BRAINSBEHINDSCREENSHOT_H

includes/api/WebSocketClient.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
#include "../../includes/api/WebSocketConnection.h"
99
#include <string>
1010

11+
constexpr int MATRIX_WIDTH = 128;
12+
constexpr int MATRIX_HEIGHT = 64;
13+
1114
class WebSocketClient {
1215
public:
1316
WebSocketClient(const std::string &address, const std::string &port, const std::string &path);
@@ -18,7 +21,9 @@ class WebSocketClient {
1821

1922
void reset() const;
2023

21-
static void startStream();
24+
static void takeScreenShot();
25+
26+
void startStream();
2227

2328
void run();
2429

includes/enums/Commands.h

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ enum Commands {
1111
EXIT,
1212
ALL,
1313
RESET,
14+
TAKE,
1415
STREAM,
1516
HELP,
1617
UNKNOWN
@@ -26,6 +27,9 @@ inline Commands stringToCommand(const std::string &commandStr) {
2627
if (commandStr == "reset") {
2728
return RESET;
2829
}
30+
if (commandStr == "take") {
31+
return TAKE;
32+
}
2933
if (commandStr == "stream") {
3034
return STREAM;
3135
}
@@ -39,6 +43,7 @@ inline void printHelp() {
3943
std::cout << "Available commands:\n"
4044
<< " exit - Terminate the program\n"
4145
<< " all - Test all pixels\n"
46+
<< " take - Take a screenshot and save it\n"
4247
<< " reset - Turn off all pixels\n"
4348
<< " stream - Start streaming\n"
4449
<< " help - Show this help message\n";

main.cpp

-6
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,8 @@
33
//
44

55
#include "includes/api/WebSocketClient.h"
6-
#include "includes/BrainsBehindScreenshot.h"
76

87
int main() {
9-
// fingers crossed lol
10-
ScreenCapture screencapture = ScreenCapture();
11-
auto bmp = screencapture.CaptureScreenBitmap();
12-
13-
148
WebSocketClient client("localhost", "8080", "/publisher/matrixMarco");
159
client.run();
1610
return 0;

src/BitmapConverter.cpp

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
//
2+
// Created by marco on 16/11/2024.
3+
//
4+
5+
#include "../includes/BitmapConverter.h"
6+
#include <iostream>
7+
#include <sstream>
8+
9+
std::vector<BYTE> BitmapConverter::HBitmapToRGB(HBITMAP hBitmap, int width, int height) {
10+
BITMAP bmp;
11+
GetObject(hBitmap, sizeof(BITMAP), &bmp);
12+
13+
BITMAPINFOHEADER bi;
14+
bi.biSize = sizeof(BITMAPINFOHEADER);
15+
bi.biWidth = width;
16+
bi.biHeight = -height; // Negative height to avoid flipping
17+
bi.biPlanes = 1;
18+
bi.biBitCount = 24; // Use 24-bit RGB
19+
bi.biCompression = BI_RGB;
20+
21+
std::vector<BYTE> pixels(width * height * 3);
22+
HDC hdc = GetDC(nullptr);
23+
GetDIBits(hdc, hBitmap, 0, height, pixels.data(), reinterpret_cast<BITMAPINFO *>(&bi), DIB_RGB_COLORS);
24+
ReleaseDC(nullptr, hdc);
25+
26+
return pixels;
27+
}
28+
29+
std::vector<BYTE> BitmapConverter::ResizeImage(const std::vector<BYTE> &pixels, const int originalWidth,
30+
const int originalHeight, const int newWidth, const int newHeight) {
31+
std::vector<BYTE> resized(newWidth * newHeight * 3);
32+
33+
for (int y = 0; y < newHeight; ++y) {
34+
for (int x = 0; x < newWidth; ++x) {
35+
int nearestX = static_cast<int>(x * (originalWidth / static_cast<float>(newWidth)));
36+
int nearestY = static_cast<int>(y * (originalHeight / static_cast<float>(newHeight)));
37+
38+
int originalIdx = (nearestY * originalWidth + nearestX) * 3;
39+
int resizedIdx = (y * newWidth + x) * 3;
40+
41+
resized[resizedIdx] = pixels[originalIdx];
42+
resized[resizedIdx + 1] = pixels[originalIdx + 1];
43+
resized[resizedIdx + 2] = pixels[originalIdx + 2];
44+
}
45+
}
46+
47+
return resized;
48+
}
49+
50+
std::vector<int> BitmapConverter::CategorizePixels(const std::vector<BYTE> &pixels, int width, int height) {
51+
std::vector<int> categorizedPixels(width * height);
52+
53+
for (int y = 0; y < height; ++y) {
54+
for (int x = 0; x < width; ++x) {
55+
int idx = (y * width + x) * 3;
56+
BYTE blue = pixels[idx];
57+
BYTE green = pixels[idx + 1];
58+
BYTE red = pixels[idx + 2];
59+
60+
// convert to grayscale using the luminance formula
61+
BYTE gray = static_cast<BYTE>(0.299 * red + 0.587 * green + 0.114 * blue);
62+
63+
// categorize based on grayscale value
64+
int category;
65+
if (gray < 63.75) {
66+
category = 0; // Off
67+
} else if (gray < 127.5) {
68+
category = 1; // Red
69+
} else if (gray < 191.25) {
70+
category = 3; // Green
71+
} else {
72+
category = 2; // Orange
73+
}
74+
75+
categorizedPixels[y * width + x] = category;
76+
}
77+
}
78+
79+
return categorizedPixels;
80+
}
81+
82+
std::string BitmapConverter::PixelsToString(const std::vector<int>& pixels) {
83+
std::ostringstream oss;
84+
for (const int value : pixels) {
85+
oss << value;
86+
}
87+
return oss.str();
88+
}

src/BrainsBehindScreenshot.cpp

+9-20
Original file line numberDiff line numberDiff line change
@@ -24,43 +24,32 @@ void ScreenCapture::ShutdownGDIPlus() const {
2424
GdiplusShutdown(gdiplusToken);
2525
}
2626

27-
BITMAP ScreenCapture::CaptureScreenBitmap() {
27+
HBITMAP ScreenCapture::CaptureScreenBitmap(int& width, int& height) {
2828
// Get screen dimensions
2929
HDC screenDC = GetDC(nullptr); // Get the device context of the screen
3030
HDC memoryDC = CreateCompatibleDC(screenDC);
3131

32-
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
33-
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
32+
width = GetSystemMetrics(SM_CXSCREEN);
33+
height = GetSystemMetrics(SM_CYSCREEN);
3434

3535
// Create a compatible bitmap
36-
HBITMAP hBitmap = CreateCompatibleBitmap(screenDC, screenWidth, screenHeight);
36+
HBITMAP hBitmap = CreateCompatibleBitmap(screenDC, width, height);
3737

3838
if (!hBitmap) {
3939
std::wcerr << L"Failed to create compatible bitmap." << std::endl;
4040
DeleteDC(memoryDC);
4141
ReleaseDC(nullptr, screenDC);
42-
43-
BITMAP emptyBitmap = {0};
44-
return emptyBitmap;
42+
return nullptr;
4543
}
4644

47-
// Select the bitmap into the memory DC
48-
auto oldBitmap = (HBITMAP)SelectObject(memoryDC, hBitmap);
49-
50-
// Copy screen content to the bitmap
51-
BitBlt(memoryDC, 0, 0, screenWidth, screenHeight, screenDC, 0, 0, SRCCOPY);
52-
53-
//Convert HBITMAP to BITMAP
54-
BITMAP bmp;
55-
GetObject(hBitmap, sizeof(bmp), &bmp);
56-
57-
// Clean up
45+
auto oldBitmap = static_cast<HBITMAP>(SelectObject(memoryDC, hBitmap));
46+
BitBlt(memoryDC, 0, 0, width, height, screenDC, 0, 0, SRCCOPY);
5847
SelectObject(memoryDC, oldBitmap);
59-
DeleteObject(hBitmap);
48+
6049
DeleteDC(memoryDC);
6150
ReleaseDC(nullptr, screenDC);
6251

63-
return bmp;
52+
return hBitmap;
6453
}
6554

6655
bool ScreenCapture::CaptureScreen(const std::wstring& filename) {

src/api/WebSocketClient.cpp

+23-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include <iostream>
66
#include <sstream>
7+
#include "../../includes/BitmapConverter.h"
78
#include "../../includes/api/WebSocketClient.h"
89
#include "../../includes/enums/Commands.h"
910
#include "../../includes/BrainsBehindScreenshot.h"
@@ -25,18 +26,36 @@ void WebSocketClient::reset() const {
2526
connection.sendMessage(R"({"type":"clear"})");
2627
}
2728

28-
void WebSocketClient::startStream() {
29-
ScreenCapture screenCapture;
30-
29+
void WebSocketClient::takeScreenShot() {
3130
std::wstring filename = L"screenshot.bmp";
3231

33-
if (screenCapture.CaptureScreen(filename)) {
32+
if (ScreenCapture::CaptureScreen(filename)) {
3433
std::wcout << L"Screenshot saved to " << filename << std::endl;
3534
} else {
3635
std::wcerr << L"Failed to capture the screen." << std::endl;
3736
}
3837
}
3938

39+
void WebSocketClient::startStream() {
40+
int width, height;
41+
42+
HBITMAP hBitmap = ScreenCapture::CaptureScreenBitmap(width, height);
43+
if (hBitmap == nullptr) {
44+
std::cerr << "Screen capture failed." << std::endl;
45+
return;
46+
}
47+
48+
const auto pixels = BitmapConverter::HBitmapToRGB(hBitmap, width, height);
49+
const auto resizedPixels = BitmapConverter::ResizeImage(pixels, width, height, MATRIX_WIDTH, MATRIX_HEIGHT);
50+
const auto categorizedPixels = BitmapConverter::CategorizePixels(resizedPixels, MATRIX_WIDTH, MATRIX_HEIGHT);
51+
52+
const std::string pixelString = BitmapConverter::PixelsToString(categorizedPixels);
53+
54+
connection.sendMessage(R"({"type":"allpixels","data":")" + pixelString + R"("})");
55+
56+
DeleteObject(hBitmap);
57+
}
58+
4059
void WebSocketClient::run() {
4160
WSADATA wsaData;
4261
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {

src/api/WebSocketConnection.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ void WebSocketConnection::sendMessage(const std::string &message) const {
143143
send(sock, reinterpret_cast<const char *>(frame.data()), frame.size(), 0);
144144
}
145145

146+
// TODO: check/rework that because it throws an error if the message is too long
146147
std::string WebSocketConnection::receiveMessage() const {
147148
char buffer[BUFFER_SIZE];
148149
int bytes_received = recv(sock, buffer, BUFFER_SIZE, 0);

0 commit comments

Comments
 (0)