Refactoring shitcode (stage 2)

This commit is contained in:
Andrew Golovashevich 2024-10-09 15:17:18 +03:00
parent 01ae77c9a9
commit 6d10eb315b
4 changed files with 123 additions and 96 deletions

View File

@ -42,16 +42,16 @@ public:
} }
void SetPixel(int x, int y, COLOR color) { void set_pixel(int x, int y, COLOR color) {
matrix[y][x] = color; matrix[y][x] = color;
} }
COLOR GetPixel(int x, int y) { COLOR get_pixel(int x, int y) {
return matrix[y][x]; return matrix[y][x];
} }
void Circle(int x0, int y0, int radius, COLOR color) { void draw_circle(int x0, int y0, int radius, COLOR outline) {
int x = 0, y = radius; int x = 0, y = radius;
while (x < y) { while (x < y) {
// Определяем, какая точка (пиксель): (x, y) или (x, y - 1) ближе к линии окружности // Определяем, какая точка (пиксель): (x, y) или (x, y - 1) ближе к линии окружности
@ -63,20 +63,20 @@ public:
y--; y--;
// Перенос и отражение вычисленных координат на все октанты окружности // Перенос и отражение вычисленных координат на все октанты окружности
SetPixel(x0 + x, y0 + y, color); set_pixel(x0 + x, y0 + y, outline);
SetPixel(x0 + x, y0 - y, color); set_pixel(x0 + x, y0 - y, outline);
SetPixel(x0 + y, y0 + x, color); set_pixel(x0 + y, y0 + x, outline);
SetPixel(x0 + y, y0 - x, color); set_pixel(x0 + y, y0 - x, outline);
SetPixel(x0 - x, y0 + y, color); set_pixel(x0 - x, y0 + y, outline);
SetPixel(x0 - x, y0 - y, color); set_pixel(x0 - x, y0 - y, outline);
SetPixel(x0 - y, y0 + x, color); set_pixel(x0 - y, y0 + x, outline);
SetPixel(x0 - y, y0 - x, color); set_pixel(x0 - y, y0 - x, outline);
x++; x++;
} }
} }
void DrawLine(int x1, int y1, int x2, int y2, COLOR color) { void draw_line(int x1, int y1, int x2, int y2, COLOR color) {
int dy = y2 - y1, dx = x2 - x1; int dy = y2 - y1, dx = x2 - x1;
if (dx == 0 && dy == 0) { if (dx == 0 && dy == 0) {
matrix[y1][x1] = color; matrix[y1][x1] = color;

View File

@ -27,8 +27,9 @@ int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInst, LPSTR lpszArgs,
wcl.hCursor = LoadCursor(nullptr, IDC_ARROW); // Курсор wcl.hCursor = LoadCursor(nullptr, IDC_ARROW); // Курсор
wcl.lpszMenuName = nullptr; // Без меню wcl.lpszMenuName = nullptr; // Без меню
wcl.cbClsExtra = 0; // Без дополнительной информации wcl.cbClsExtra = 0; // Без дополнительной информации
wcl.cbWndExtra = sizeof(Painter *); wcl.cbWndExtra = sizeof(PainterState *) + sizeof(Painter **);
static_assert(sizeof(Painter *) == sizeof(LONG_PTR)); static_assert(sizeof(PainterState *) == sizeof(LONG_PTR));
static_assert(sizeof(Painter **) == sizeof(LONG_PTR));
wcl.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); //Белый фон wcl.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); //Белый фон
@ -74,22 +75,18 @@ int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInst, LPSTR lpszArgs,
} }
static Painter painter;
// Следующая функция вызывается операционной системой Windows и получает в качестве // Следующая функция вызывается операционной системой Windows и получает в качестве
// параметров сообщения из очереди сообщений данного приложения // параметров сообщения из очереди сообщений данного приложения
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
static int pixelSize = 8; // Размер "большого" пикселя static int pixelSize = 8; // Размер "большого" пикселя
switch (message) { switch (message) {
case WM_CREATE: { case WM_CREATE:
SetTimer(hWnd, 1, 1000 / 30, nullptr); SetTimer(hWnd, 1, 1000 / 30, nullptr);
SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(new Painter{})); SetWindowLongPtr(hWnd, 0, (LONG_PTR) new PainterState());
} SetWindowLongPtr(hWnd, sizeof(PainterState *), (LONG_PTR) &predefined_painters[0]);
break; break;
// Обработка сообщения на перерисовку окна
case WM_PAINT: { case WM_PAINT: {
PAINTSTRUCT ps; PAINTSTRUCT ps;
@ -110,8 +107,10 @@ LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPara
int H = (height - 22) / ratio; // Отнимем высоту StatusBar'а int H = (height - 22) / ratio; // Отнимем высоту StatusBar'а
Frame frame(W, H); Frame frame(W, H);
auto *painter = reinterpret_cast<Painter *>(GetWindowLongPtr(hWnd, 0)); auto *state = reinterpret_cast<PainterState *>(GetWindowLongPtr(hWnd, 0));
painter->Draw(frame); auto *painter = *reinterpret_cast<Painter **>(GetWindowLongPtr(hWnd, sizeof(PainterState *)));
PixelGridPainter{COLOR{245, 245, 245}, COLOR{240, 240, 240}}.draw(state, &frame);
painter->draw(state, &frame);
// Системная структура для хранения цвета пикселя // Системная структура для хранения цвета пикселя
// Буфер кадра, который будет передаваться операционной системе, должен состоять из массива этих структур // Буфер кадра, который будет передаваться операционной системе, должен состоять из массива этих структур
@ -132,7 +131,7 @@ LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPara
for (int y = 0; y < H * ratio; y++) for (int y = 0; y < H * ratio; y++)
for (int x = 0; x < W * ratio; x++) { for (int x = 0; x < W * ratio; x++) {
RGBPIXEL *pixel = bitmap + y * width + x; RGBPIXEL *pixel = bitmap + y * width + x;
COLOR color = frame.GetPixel(x / ratio, y / ratio); COLOR color = frame.get_pixel(x / ratio, y / ratio);
pixel->RED = color.red; pixel->RED = color.red;
pixel->GREEN = color.green; pixel->GREEN = color.green;
pixel->BLUE = color.blue; pixel->BLUE = color.blue;
@ -190,8 +189,8 @@ LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPara
break; break;
case WM_LBUTTONDOWN: case WM_LBUTTONDOWN:
reinterpret_cast<Painter *>(GetWindowLongPtr(hWnd, 0))->SetClickedPixel(LOWORD(lParam) / pixelSize, HIWORD(lParam) / pixelSize); reinterpret_cast<PainterState *>(GetWindowLongPtr(hWnd, 0))->set_clicked_pixel(LOWORD(lParam) / pixelSize, HIWORD(lParam) / pixelSize);
InvalidateRect(hWnd, NULL, false); InvalidateRect(hWnd, nullptr, false);
break; break;
case WM_KEYDOWN: case WM_KEYDOWN:
@ -199,7 +198,7 @@ LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPara
if (pixelSize > 1 && wParam == VK_F2) pixelSize--; if (pixelSize > 1 && wParam == VK_F2) pixelSize--;
if (pixelSize < 64 && wParam == VK_F3) pixelSize++; if (pixelSize < 64 && wParam == VK_F3) pixelSize++;
InvalidateRect(hWnd, NULL, false); InvalidateRect(hWnd, nullptr, false);
char str[256]; char str[256];
sprintf_s(str, "Масштаб (F2/F3): %d", pixelSize); sprintf_s(str, "Масштаб (F2/F3): %d", pixelSize);
@ -208,6 +207,14 @@ LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPara
if (wParam == VK_F1) { if (wParam == VK_F1) {
MessageBoxA(hWnd, "Работу выполнил студент группы ПВ-221 Колесников А.И.", "О программе", MB_ICONINFORMATION); MessageBoxA(hWnd, "Работу выполнил студент группы ПВ-221 Колесников А.И.", "О программе", MB_ICONINFORMATION);
} }
if (wParam == VK_F4) {
auto newPtr = reinterpret_cast<Painter const *const *>(GetWindowLongPtr(hWnd, sizeof(PainterState *)));
newPtr++;
if (newPtr == nullptr)
newPtr = &predefined_painters[0];
SetWindowLongPtr(hWnd, sizeof(PainterState *), (LONG_PTR) newPtr);
}
break; break;
// Обработка сообщения на изменение размера окна // Обработка сообщения на изменение размера окна
@ -217,13 +224,13 @@ LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPara
break; break;
case WM_TIMER: case WM_TIMER:
reinterpret_cast<Painter *>(GetWindowLongPtr(hWnd, 0))->AngleStep(); reinterpret_cast<PainterState *>(GetWindowLongPtr(hWnd, 0))->angle += 0.05;
InvalidateRect(hWnd, nullptr, false); InvalidateRect(hWnd, nullptr, false);
break; break;
case WM_DESTROY: case WM_DESTROY:
delete reinterpret_cast<Painter *>(GetWindowLongPtr(hWnd, 0)); delete reinterpret_cast<PainterState *>(GetWindowLongPtr(hWnd, 0));
SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(nullptr)); SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(nullptr));
PostQuitMessage(0); PostQuitMessage(0);
break; break;

View File

@ -1,59 +1,61 @@
#include <tuple>
#include "frame.hpp" #include "frame.hpp"
#include "painter.hpp" #include "painter.hpp"
void Painter::Draw(Frame &frame) { class DemoPainter : public Painter {
// Шахматная текстура
for (int y = 0; y < frame.height; y++) void draw(PainterState *state, Frame *frame) const override {
for (int x = 0; x < frame.width; x++) {
if ((x + y) % 2 == 0) int W = frame->width, H = frame->height;
frame.SetPixel(x, y, {230, 255, 230}); // Золотистый цвет // Размер рисунка возьмём меньше (7 / 8), чтобы он не касался границ экрана
//frame.SetPixel(x, y, { 217, 168, 14 }); float a = 7.0f / 8 * ((W < H) ? W - 1 : H - 1) / sqrt(2);
else if (a < 1) return; // Если окно очень маленькое, то ничего не рисуем
frame.SetPixel(x, y, {200, 200, 200}); // Чёрный цвет float angle = state->angle; // Угол поворота
//frame.SetPixel(x, y, { 255, 255, 255 }); // Белый цвет a = a / 2;
// Инициализируем исходные координаты центра и вершин квадрата
struct {
float x;
float y;
} C = {(float) W / 2, (float) H / 2}, A[4] = {{C.x + a, C.y + a},
{C.x + a, C.y - a},
{C.x - a, C.y - a},
{C.x - a, C.y + a}};
// Поворачиваем все вершины квадрата вокруг точки C на угол angle
for (int i = 0; i < 4; i++) {
float xi = A[i].x, yi = A[i].y;
A[i].x = (xi - C.x) * cos(angle) - (yi - C.y) * sin(angle) + C.x;
A[i].y = (xi - C.x) * sin(angle) + (yi - C.y) * cos(angle) + C.y;
} }
// Рисуем стороны квадрата
for (int i = 0; i < 4; i++) {
int i2 = (i + 1) % 4;
frame->draw_line( // Добавляем везде 0.5f, чтобы вещественные числа правильно округлялись при преобразовании к целому типу
int(A[i].x + 0.5f),
int(A[i].y + 0.5f),
int(A[i2].x + 0.5f),
int(A[i2].y + 0.5f), COLOR(200, 30, 45));
}
int W = frame.width, H = frame.height; // Рисуем описанную окружность
// Размер рисунка возьмём меньше (7 / 8), чтобы он не касался границ экрана frame->draw_circle((int) C.x, (int) C.y, int(a * sqrt(2) + 0.5f), COLOR(100, 100, 250));
float a = 7.0f / 8 * ((W < H) ? W - 1 : H - 1) / sqrt(2);
if (a < 1) return; // Если окно очень маленькое, то ничего не рисуем
float angle = this->global_angle; // Угол поворота
a = a / 2;
// Инициализируем исходные координаты центра и вершин квадрата // Рисуем пиксель, на который кликнул пользователь
struct { if (state->clicked_pixel.x >= 0 && state->clicked_pixel.x < W &&
float x; state->clicked_pixel.y >= 0 && state->clicked_pixel.y < H)
float y; frame->set_pixel(state->clicked_pixel.x, state->clicked_pixel.y, {34, 175, 60}); // Пиксель зелёного цвета
} C = {(float) W / 2, (float) H / 2}, A[4] = {{C.x + a, C.y + a},
{C.x + a, C.y - a},
{C.x - a, C.y - a},
{C.x - a, C.y + a}};
// Поворачиваем все вершины квадрата вокруг точки C на угол angle
for (int i = 0; i < 4; i++) {
float xi = A[i].x, yi = A[i].y;
A[i].x = (xi - C.x) * cos(angle) - (yi - C.y) * sin(angle) + C.x;
A[i].y = (xi - C.x) * sin(angle) + (yi - C.y) * cos(angle) + C.y;
} }
};
// Рисуем стороны квадрата static auto predefined_painters_ = std::make_tuple(
for (int i = 0; i < 4; i++) { DemoPainter()
int i2 = (i + 1) % 4; );
frame.DrawLine( // Добавляем везде 0.5f, чтобы вещественные числа правильно округлялись при преобразовании к целому типу
int(A[i].x + 0.5f),
int(A[i].y + 0.5f),
int(A[i2].x + 0.5f),
int(A[i2].y + 0.5f), COLOR(200, 30, 45));
}
// Рисуем описанную окружность Painter const *const predefined_painters[] = {
frame.Circle((int) C.x, (int) C.y, int(a * sqrt(2) + 0.5f), COLOR(100, 100, 250)); &std::get<0>(predefined_painters_),
nullptr
// Рисуем пиксель, на который кликнул пользователь };
if (global_clicked_pixel.X >= 0 && global_clicked_pixel.X < W &&
global_clicked_pixel.Y >= 0 && global_clicked_pixel.Y < H)
frame.SetPixel(global_clicked_pixel.X, global_clicked_pixel.Y, {34, 175, 60}); // Пиксель зелёного цвета
}

View File

@ -2,26 +2,44 @@
#include "frame.hpp" #include "frame.hpp"
class PainterState {
public:
float angle = 0;
struct {
unsigned short x = -1, y = -1;
} clicked_pixel;
PainterState() = default;
void set_clicked_pixel(unsigned short x, unsigned short y) {
this->clicked_pixel.x = x;
this->clicked_pixel.y = y;
}
};
class Painter { class Painter {
private:
float global_angle = 0;
struct {
int X, Y;
} global_clicked_pixel = {-1, -1};
public: public:
virtual void draw(PainterState *state, Frame *frame) const = 0;
void SetClickedPixel(int x, int y) {
this->global_clicked_pixel.X = x;
this->global_clicked_pixel.Y = y;
}
void AngleStep() {
this->global_angle += 0.05;
}
void Draw(Frame &frame);
}; };
extern Painter const *const predefined_painters[];
class PixelGridPainter : public Painter {
private:
const COLOR c1;
const COLOR c2;
public:
inline PixelGridPainter(COLOR c1, COLOR c2) : c1{c1}, c2{c2} {}
inline void draw(PainterState *state, Frame *frame) const override {
for (int y = 0; y < frame->height; y++) {
for (int x = 0; x < frame->width; x++) {
if ((x + y) % 2 == 0)
frame->set_pixel(x, y, this->c1);
else
frame->set_pixel(x, y, this->c2);
}
}
}
};