Refactoring shitcode (stage 1)
This commit is contained in:
parent
81295e0514
commit
01ae77c9a9
@ -9,8 +9,9 @@ add_executable(
|
||||
|
||||
WIN32
|
||||
|
||||
src/Frame.h
|
||||
src/frame.hpp
|
||||
src/main.cpp
|
||||
src/Painter.h
|
||||
src/painter.cpp
|
||||
src/painter.hpp
|
||||
src/shaders.cpp
|
||||
)
|
164
src/Frame.h
164
src/Frame.h
@ -1,164 +0,0 @@
|
||||
#ifndef FRAME_H
|
||||
#define FRAME_H
|
||||
|
||||
#include <math.h>
|
||||
|
||||
// Cтруктура для задания цвета
|
||||
typedef struct tagCOLOR
|
||||
{
|
||||
unsigned char RED; // Компонента красного цвета
|
||||
unsigned char GREEN; // Компонента зелёного цвета
|
||||
unsigned char BLUE; // Компонента синего цвета
|
||||
unsigned char ALPHA; // Прозрачность (альфа канал)
|
||||
|
||||
tagCOLOR() : RED(0), GREEN(0), BLUE(0), ALPHA(255) { }
|
||||
tagCOLOR(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha = 255) : RED(red), GREEN(green), BLUE(blue), ALPHA(alpha) { }
|
||||
|
||||
} COLOR;
|
||||
|
||||
|
||||
template<typename TYPE> void swap(TYPE& a, TYPE& b)
|
||||
{
|
||||
TYPE t = a;
|
||||
a = b;
|
||||
b = t;
|
||||
}
|
||||
|
||||
|
||||
// Буфер кадра
|
||||
class Frame
|
||||
{
|
||||
// Указатель на массив пикселей
|
||||
// Буфер кадра будет представлять собой матрицу, которая располагается в памяти в виде непрерывного блока
|
||||
COLOR* pixels;
|
||||
|
||||
// Указатели на строки пикселей буфера кадра
|
||||
COLOR** matrix;
|
||||
|
||||
public:
|
||||
|
||||
// Размеры буфера кадра
|
||||
int width, height;
|
||||
|
||||
Frame(int _width, int _height) : width(_width), height(_height)
|
||||
{
|
||||
int size = width * height;
|
||||
|
||||
// Создание буфера кадра в виде непрерывной матрицы пикселей
|
||||
pixels = new COLOR[size];
|
||||
|
||||
// Указатели на строки пикселей запишем в отдельный массив
|
||||
matrix = new COLOR* [height];
|
||||
|
||||
// Инициализация массива указателей
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
matrix[i] = pixels + i * width;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Задаёт цвет color пикселю с координатами (x, y)
|
||||
void SetPixel(int x, int y, COLOR color)
|
||||
{
|
||||
matrix[y][x] = color;
|
||||
}
|
||||
|
||||
// Возвращает цвет пикселя с координатами (x, y)
|
||||
COLOR GetPixel(int x, int y)
|
||||
{
|
||||
return matrix[y][x];
|
||||
}
|
||||
|
||||
|
||||
// Рисование окружности
|
||||
void Circle(int x0, int y0, int radius, COLOR color)
|
||||
{
|
||||
int x = 0, y = radius;
|
||||
while(x < y)
|
||||
{
|
||||
// Определяем, какая точка (пиксель): (x, y) или (x, y - 1) ближе к линии окружности
|
||||
int D1 = x * x + y * y - radius * radius;
|
||||
int D2 = x * x + (y - 1) * (y - 1) - radius * radius;
|
||||
|
||||
// Если ближе точка (x, y - 1), то смещаемся к ней
|
||||
if (D1 > -D2)
|
||||
y--;
|
||||
|
||||
// Перенос и отражение вычисленных координат на все октанты окружности
|
||||
SetPixel(x0 + x, y0 + y, color);
|
||||
SetPixel(x0 + x, y0 - y, color);
|
||||
SetPixel(x0 + y, y0 + x, color);
|
||||
SetPixel(x0 + y, y0 - x, color);
|
||||
SetPixel(x0 - x, y0 + y, color);
|
||||
SetPixel(x0 - x, y0 - y, color);
|
||||
SetPixel(x0 - y, y0 + x, color);
|
||||
SetPixel(x0 - y, y0 - x, color);
|
||||
x++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Рисование отрезка
|
||||
void DrawLine(int x1, int y1, int x2, int y2, COLOR color)
|
||||
{
|
||||
int dy = y2 - y1, dx = x2 - x1;
|
||||
if (dx == 0 && dy == 0)
|
||||
{
|
||||
matrix[y1][x1] = color;
|
||||
return;
|
||||
}
|
||||
|
||||
if (abs(dx) > abs(dy))
|
||||
{
|
||||
if (x2 < x1)
|
||||
{
|
||||
// Обмен местами точек (x1, y1) и (x2, y2)
|
||||
swap(x1, x2);
|
||||
swap(y1, y2);
|
||||
dx = -dx; dy = -dy;
|
||||
}
|
||||
|
||||
int y, dx2 = dx / 2, p = 0;
|
||||
if (dy < 0) dx2 = -dx2;
|
||||
for (int x = x1; x <= x2; x++)
|
||||
{
|
||||
// y = (dy * (x - x1) + dx2) / dx + y1;
|
||||
y = (p + dx2) / dx + y1;
|
||||
p += dy;
|
||||
matrix[y][x] = color;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (y2 < y1)
|
||||
{
|
||||
// Обмен местами точек (x1, y1) и (x2, y2)
|
||||
swap(x1, x2);
|
||||
swap(y1, y2);
|
||||
dx = -dx; dy = -dy;
|
||||
}
|
||||
|
||||
int x, dy2 = dy / 2, p = 0;
|
||||
if (dx < 0) dy2 = -dy2;
|
||||
for (int y = y1; y <= y2; y++)
|
||||
{
|
||||
// x = (dx * (y - y1) + dy2) / dy + x1;
|
||||
x = (p + dy2) / dy + x1;
|
||||
p += dx;
|
||||
matrix[y][x] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
~Frame(void)
|
||||
{
|
||||
delete []pixels;
|
||||
delete []matrix;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // FRAME_H
|
@ -1,80 +0,0 @@
|
||||
#ifndef PAINTER_H
|
||||
#define PAINTER_H
|
||||
|
||||
#include "Frame.h"
|
||||
|
||||
|
||||
// Угол поворота фигуры
|
||||
float global_angle = 0;
|
||||
|
||||
// Координаты последнего пикселя, который выбрал пользователь
|
||||
struct
|
||||
{
|
||||
int X, Y;
|
||||
} global_clicked_pixel = {-1, -1};
|
||||
|
||||
|
||||
class Painter
|
||||
{
|
||||
public:
|
||||
|
||||
void Draw(Frame& frame)
|
||||
{
|
||||
// Шахматная текстура
|
||||
for (int y = 0; y < frame.height; y++)
|
||||
for (int x = 0; x < frame.width; x++)
|
||||
{
|
||||
if ((x + y) % 2 == 0)
|
||||
frame.SetPixel(x, y, { 230, 255, 230 }); // Золотистый цвет
|
||||
//frame.SetPixel(x, y, { 217, 168, 14 });
|
||||
else
|
||||
frame.SetPixel(x, y, { 200, 200, 200 }); // Чёрный цвет
|
||||
//frame.SetPixel(x, y, { 255, 255, 255 }); // Белый цвет
|
||||
}
|
||||
|
||||
|
||||
int W = frame.width, H = frame.height;
|
||||
// Размер рисунка возьмём меньше (7 / 8), чтобы он не касался границ экрана
|
||||
float a = 7.0f / 8 * ((W < H) ? W - 1 : H - 1) / sqrt(2);
|
||||
if (a < 1) return; // Если окно очень маленькое, то ничего не рисуем
|
||||
float angle = global_angle; // Угол поворота
|
||||
a = a / 2;
|
||||
|
||||
// Инициализируем исходные координаты центра и вершин квадрата
|
||||
struct
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
} C = {W / 2, 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.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));
|
||||
}
|
||||
|
||||
// Рисуем описанную окружность
|
||||
frame.Circle((int)C.x, (int)C.y, int(a*sqrt(2) + 0.5f), COLOR(100, 100, 250));
|
||||
|
||||
// Рисуем пиксель, на который кликнул пользователь
|
||||
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 }); // Пиксель зелёного цвета
|
||||
}
|
||||
};
|
||||
|
||||
#endif // PAINTER_H
|
125
src/frame.hpp
Normal file
125
src/frame.hpp
Normal file
@ -0,0 +1,125 @@
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
|
||||
struct COLOR {
|
||||
unsigned char red;
|
||||
unsigned char green;
|
||||
unsigned char blue;
|
||||
unsigned char alpha;
|
||||
|
||||
COLOR() : red(0), green(0), blue(0), alpha(255) {}
|
||||
|
||||
COLOR(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha = 255) : red(red), green(green), blue(blue), alpha(alpha) {}
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
template<typename TYPE>
|
||||
void swap(TYPE &a, TYPE &b) {
|
||||
TYPE t = a;
|
||||
a = b;
|
||||
b = t;
|
||||
}
|
||||
|
||||
|
||||
class Frame {
|
||||
COLOR *pixels;
|
||||
|
||||
COLOR **matrix;
|
||||
public:
|
||||
int width, height;
|
||||
Frame(int _width, int _height) : width(_width), height(_height) {
|
||||
int size = width * height;
|
||||
|
||||
pixels = new COLOR[size];
|
||||
|
||||
matrix = new COLOR *[height];
|
||||
|
||||
for (int i = 0; i < height; i++) {
|
||||
matrix[i] = pixels + i * width;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SetPixel(int x, int y, COLOR color) {
|
||||
matrix[y][x] = color;
|
||||
}
|
||||
|
||||
COLOR GetPixel(int x, int y) {
|
||||
return matrix[y][x];
|
||||
}
|
||||
|
||||
|
||||
void Circle(int x0, int y0, int radius, COLOR color) {
|
||||
int x = 0, y = radius;
|
||||
while (x < y) {
|
||||
// Определяем, какая точка (пиксель): (x, y) или (x, y - 1) ближе к линии окружности
|
||||
int D1 = x * x + y * y - radius * radius;
|
||||
int D2 = x * x + (y - 1) * (y - 1) - radius * radius;
|
||||
|
||||
// Если ближе точка (x, y - 1), то смещаемся к ней
|
||||
if (D1 > -D2)
|
||||
y--;
|
||||
|
||||
// Перенос и отражение вычисленных координат на все октанты окружности
|
||||
SetPixel(x0 + x, y0 + y, color);
|
||||
SetPixel(x0 + x, y0 - y, color);
|
||||
SetPixel(x0 + y, y0 + x, color);
|
||||
SetPixel(x0 + y, y0 - x, color);
|
||||
SetPixel(x0 - x, y0 + y, color);
|
||||
SetPixel(x0 - x, y0 - y, color);
|
||||
SetPixel(x0 - y, y0 + x, color);
|
||||
SetPixel(x0 - y, y0 - x, color);
|
||||
x++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DrawLine(int x1, int y1, int x2, int y2, COLOR color) {
|
||||
int dy = y2 - y1, dx = x2 - x1;
|
||||
if (dx == 0 && dy == 0) {
|
||||
matrix[y1][x1] = color;
|
||||
return;
|
||||
}
|
||||
|
||||
if (abs(dx) > abs(dy)) {
|
||||
if (x2 < x1) {
|
||||
swap(x1, x2);
|
||||
swap(y1, y2);
|
||||
dx = -dx;
|
||||
dy = -dy;
|
||||
}
|
||||
|
||||
int y, dx2 = dx / 2, p = 0;
|
||||
if (dy < 0) dx2 = -dx2;
|
||||
for (int x = x1; x <= x2; x++) {
|
||||
y = (p + dx2) / dx + y1;
|
||||
p += dy;
|
||||
matrix[y][x] = color;
|
||||
}
|
||||
} else {
|
||||
if (y2 < y1) {
|
||||
swap(x1, x2);
|
||||
swap(y1, y2);
|
||||
dx = -dx;
|
||||
dy = -dy;
|
||||
}
|
||||
|
||||
int x, dy2 = dy / 2, p = 0;
|
||||
if (dx < 0) dy2 = -dy2;
|
||||
for (int y = y1; y <= y2; y++) {
|
||||
x = (p + dy2) / dy + x1;
|
||||
p += dx;
|
||||
matrix[y][x] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
~Frame() {
|
||||
delete[]pixels;
|
||||
delete[]matrix;
|
||||
}
|
||||
|
||||
};
|
172
src/main.cpp
172
src/main.cpp
@ -4,8 +4,8 @@
|
||||
#include <Windows.h>
|
||||
#include <commctrl.h>
|
||||
#include "stdio.h"
|
||||
#include "Frame.h"
|
||||
#include "Painter.h"
|
||||
#include "frame.hpp"
|
||||
#include "painter.hpp"
|
||||
|
||||
// Windows-приложение для создания буфера кадра
|
||||
|
||||
@ -13,8 +13,7 @@ LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);
|
||||
|
||||
HWND hWndStatusBar; // Дескриптор компонента StatusBar
|
||||
|
||||
int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInst, LPSTR lpszArgs, int nWinMode)
|
||||
{
|
||||
int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInst, LPSTR lpszArgs, int nWinMode) {
|
||||
char szWinName[] = "Graphics Window Class"; // Имя класса окна
|
||||
|
||||
HWND hWnd; // Дескриптор главного окна
|
||||
@ -24,46 +23,48 @@ int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInst, LPSTR lpszArgs,
|
||||
wcl.lpszClassName = szWinName;// Имя класса окна
|
||||
wcl.lpfnWndProc = WindowProc; // Функция обработки сообщений
|
||||
wcl.style = 0; // Стиль по умолчанию
|
||||
wcl.hIcon = LoadIcon(NULL, IDI_APPLICATION);// Иконка
|
||||
wcl.hCursor = LoadCursor(NULL, IDC_ARROW); // Курсор
|
||||
wcl.lpszMenuName = NULL; // Без меню
|
||||
wcl.hIcon = LoadIcon(nullptr, IDI_APPLICATION);// Иконка
|
||||
wcl.hCursor = LoadCursor(nullptr, IDC_ARROW); // Курсор
|
||||
wcl.lpszMenuName = nullptr; // Без меню
|
||||
wcl.cbClsExtra = 0; // Без дополнительной информации
|
||||
wcl.cbWndExtra = 0;
|
||||
wcl.cbWndExtra = sizeof(Painter *);
|
||||
static_assert(sizeof(Painter *) == sizeof(LONG_PTR));
|
||||
|
||||
wcl.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); //Белый фон
|
||||
|
||||
wcl.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); //Белый фон
|
||||
|
||||
if (!RegisterClassA(&wcl)) // Регистрируем класс окна
|
||||
return 0;
|
||||
|
||||
hWnd = CreateWindowA(szWinName, // Создать окно
|
||||
"Лабораторная работа №1. Буфер кадра. Алгоритмы Брезенхейма",
|
||||
WS_OVERLAPPEDWINDOW, // Стиль окна
|
||||
CW_USEDEFAULT, // x-координата
|
||||
CW_USEDEFAULT, // y-координата
|
||||
CW_USEDEFAULT, // Ширина
|
||||
CW_USEDEFAULT, // Высота
|
||||
HWND_DESKTOP, // Без родительского окна
|
||||
NULL, // Без меню
|
||||
hThisInstance, // Дескриптор приложения
|
||||
NULL); // Без дополнительных аргументов
|
||||
"Лабораторная работа №1. Буфер кадра. Алгоритмы Брезенхейма",
|
||||
WS_OVERLAPPEDWINDOW, // Стиль окна
|
||||
CW_USEDEFAULT, // x-координата
|
||||
CW_USEDEFAULT, // y-координата
|
||||
CW_USEDEFAULT, // Ширина
|
||||
CW_USEDEFAULT, // Высота
|
||||
HWND_DESKTOP, // Без родительского окна
|
||||
nullptr, // Без меню
|
||||
hThisInstance, // Дескриптор приложения
|
||||
nullptr); // Без дополнительных аргументов
|
||||
|
||||
// Создаём компонент типа StatusBar
|
||||
hWndStatusBar = CreateWindowExA(
|
||||
0, STATUSCLASSNAMEA, NULL,
|
||||
WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP,
|
||||
0, 0, 0, 0,
|
||||
hWnd, (HMENU) 10001,
|
||||
hThisInstance, NULL
|
||||
0, STATUSCLASSNAMEA, nullptr,
|
||||
WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP,
|
||||
0, 0, 0, 0,
|
||||
hWnd, (HMENU) 10001,
|
||||
hThisInstance, nullptr
|
||||
);
|
||||
|
||||
// Настройка частей StatusBar'а
|
||||
int statwidths[] = { 150, 300, -1 };
|
||||
SendMessageA(hWndStatusBar, SB_SETPARTS, sizeof(statwidths) / sizeof(int), (LPARAM)statwidths);
|
||||
int statwidths[] = {150, 300, -1};
|
||||
SendMessageA(hWndStatusBar, SB_SETPARTS, sizeof(statwidths) / sizeof(int), (LPARAM) statwidths);
|
||||
|
||||
ShowWindow(hWnd, nWinMode); // Показать окно
|
||||
UpdateWindow(hWnd); // Перерисовать окно
|
||||
|
||||
while (GetMessage(&msg, NULL, 0, 0)) // Запустить цикл обработки сообщений
|
||||
while (GetMessage(&msg, nullptr, 0, 0)) // Запустить цикл обработки сообщений
|
||||
{
|
||||
TranslateMessage(&msg); // Разрешить использование клавиатуры
|
||||
DispatchMessage(&msg); // Вернуть управление операционной системе Windows
|
||||
@ -73,27 +74,23 @@ int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInst, LPSTR lpszArgs,
|
||||
|
||||
}
|
||||
|
||||
static Painter painter;
|
||||
|
||||
// Следующая функция вызывается операционной системой 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; // Размер "большого" пикселя
|
||||
|
||||
switch (message)
|
||||
{
|
||||
// Обработка сообщения на создание окна
|
||||
case WM_CREATE:
|
||||
{
|
||||
// Создаем таймер, посылающий сообщения
|
||||
// функции окна примерно 30 раз в секунду
|
||||
SetTimer(hWnd, 1, 1000/30, NULL);
|
||||
switch (message) {
|
||||
case WM_CREATE: {
|
||||
SetTimer(hWnd, 1, 1000 / 30, nullptr);
|
||||
SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(new Painter{}));
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
|
||||
// Обработка сообщения на перерисовку окна
|
||||
case WM_PAINT:
|
||||
{
|
||||
// Обработка сообщения на перерисовку окна
|
||||
case WM_PAINT: {
|
||||
PAINTSTRUCT ps;
|
||||
|
||||
HDC hdc = BeginPaint(hWnd, &ps);
|
||||
@ -113,35 +110,33 @@ LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPara
|
||||
int H = (height - 22) / ratio; // Отнимем высоту StatusBar'а
|
||||
|
||||
Frame frame(W, H);
|
||||
Painter painter;
|
||||
painter.Draw(frame);
|
||||
auto *painter = reinterpret_cast<Painter *>(GetWindowLongPtr(hWnd, 0));
|
||||
painter->Draw(frame);
|
||||
|
||||
// Системная структура для хранения цвета пикселя
|
||||
// Буфер кадра, который будет передаваться операционной системе, должен состоять из массива этих структур
|
||||
// Она не совпадает с порядком следования цветов в формате RBG
|
||||
typedef struct tagRGBPIXEL
|
||||
{
|
||||
unsigned char BLUE; // Компонента синего цвета
|
||||
unsigned char GREEN; // Компонента зелёного цвета
|
||||
unsigned char RED; // Компонента красного цвета
|
||||
typedef struct tagRGBPIXEL {
|
||||
unsigned char BLUE; // Компонента синего цвета
|
||||
unsigned char GREEN; // Компонента зелёного цвета
|
||||
unsigned char RED; // Компонента красного цвета
|
||||
unsigned char ALPHA; // Прозрачность
|
||||
} RGBPIXEL;
|
||||
|
||||
// Выделение памяти для второго буфера, который будет передаваться функции CreateBitmap для создания картинки
|
||||
RGBPIXEL* bitmap = (RGBPIXEL*) HeapAlloc(GetProcessHeap(), 0, width * height * sizeof(RGBPIXEL));
|
||||
RGBPIXEL *bitmap = (RGBPIXEL *) HeapAlloc(GetProcessHeap(), 0, width * height * sizeof(RGBPIXEL));
|
||||
|
||||
// Копирование массива пикселей в соответствии с системным форматом пикселя и масштабирование картинки
|
||||
// W и H - ширина и высота изображения в буфере кадра
|
||||
// ratio - коэффициент масштабирования пикселей
|
||||
for (int y = 0; y < H * ratio; y++)
|
||||
for (int x = 0; x < W * ratio; x++)
|
||||
{
|
||||
RGBPIXEL* pixel = bitmap + y * width + x;
|
||||
for (int x = 0; x < W * ratio; x++) {
|
||||
RGBPIXEL *pixel = bitmap + y * width + x;
|
||||
COLOR color = frame.GetPixel(x / ratio, y / ratio);
|
||||
pixel->RED = color.RED;
|
||||
pixel->GREEN = color.GREEN;
|
||||
pixel->BLUE = color.BLUE;
|
||||
pixel->ALPHA = color.ALPHA;
|
||||
pixel->RED = color.red;
|
||||
pixel->GREEN = color.green;
|
||||
pixel->BLUE = color.blue;
|
||||
pixel->ALPHA = color.alpha;
|
||||
}
|
||||
|
||||
|
||||
@ -159,13 +154,13 @@ LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPara
|
||||
|
||||
// Копировать содержимое из временного контекста srcHdc в основной контекст окна hdc
|
||||
BitBlt(
|
||||
hdc, // Основной контекст
|
||||
0, 0, // Координаты левого верхнего угла, от которого будет выполняться вставка
|
||||
width, // Ширина вставляемого изображения
|
||||
height, // Высота вставляемого изображения
|
||||
srcHdc, // Дескриптор временного контекста
|
||||
0, 0, // Координаты считываемого изображения
|
||||
SRCCOPY); // Параметры операции - копирование
|
||||
hdc, // Основной контекст
|
||||
0, 0, // Координаты левого верхнего угла, от которого будет выполняться вставка
|
||||
width, // Ширина вставляемого изображения
|
||||
height, // Высота вставляемого изображения
|
||||
srcHdc, // Дескриптор временного контекста
|
||||
0, 0, // Координаты считываемого изображения
|
||||
SRCCOPY); // Параметры операции - копирование
|
||||
|
||||
EndPaint(hWnd, &ps);
|
||||
|
||||
@ -175,79 +170,68 @@ LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPara
|
||||
// Удаление временного контекста
|
||||
DeleteDC(srcHdc);
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case WM_MOUSEMOVE:
|
||||
{
|
||||
case WM_MOUSEMOVE: {
|
||||
char str[256];
|
||||
|
||||
// Устанавливаем текст в разных частях StatusBar'а
|
||||
// Экранные координаты курсора мыши
|
||||
sprintf_s(str, "X = %d, Y = %d", LOWORD(lParam), HIWORD(lParam));
|
||||
SendMessageA(hWndStatusBar, SB_SETTEXTA, 2, (LPARAM)str);
|
||||
SendMessageA(hWndStatusBar, SB_SETTEXTA, 2, (LPARAM) str);
|
||||
|
||||
// Координаты пикселя в буфере кадра
|
||||
sprintf_s(str, "BX = %d, BY = %d", LOWORD(lParam) / pixelSize, HIWORD(lParam) / pixelSize);
|
||||
SendMessageA(hWndStatusBar, SB_SETTEXTA, 1, (LPARAM)str);
|
||||
SendMessageA(hWndStatusBar, SB_SETTEXTA, 1, (LPARAM) str);
|
||||
|
||||
sprintf_s(str, "Масштаб (F2/F3): %d", pixelSize);
|
||||
SendMessageA(hWndStatusBar, SB_SETTEXTA, 0, (LPARAM)str);
|
||||
SendMessageA(hWndStatusBar, SB_SETTEXTA, 0, (LPARAM) str);
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case WM_LBUTTONDOWN:
|
||||
// Запоминаем координаты пикселя, по которому щёлкнул пользователь
|
||||
global_clicked_pixel.X = LOWORD(lParam) / pixelSize;
|
||||
global_clicked_pixel.Y = HIWORD(lParam) / pixelSize;
|
||||
// Перерисовать окно
|
||||
reinterpret_cast<Painter *>(GetWindowLongPtr(hWnd, 0))->SetClickedPixel(LOWORD(lParam) / pixelSize, HIWORD(lParam) / pixelSize);
|
||||
InvalidateRect(hWnd, NULL, false);
|
||||
break;
|
||||
|
||||
case WM_KEYDOWN:
|
||||
if (wParam == VK_F2 || wParam == VK_F3)
|
||||
{
|
||||
if (pixelSize > 1 && wParam == VK_F2) pixelSize--;
|
||||
if (wParam == VK_F2 || wParam == VK_F3) {
|
||||
if (pixelSize > 1 && wParam == VK_F2) pixelSize--;
|
||||
if (pixelSize < 64 && wParam == VK_F3) pixelSize++;
|
||||
|
||||
// Перерисовать окно
|
||||
InvalidateRect(hWnd, NULL, false);
|
||||
|
||||
char str[256];
|
||||
sprintf_s(str, "Масштаб (F2/F3): %d", pixelSize);
|
||||
SendMessageA(hWndStatusBar, SB_SETTEXTA, 0, (LPARAM)str);
|
||||
SendMessageA(hWndStatusBar, SB_SETTEXTA, 0, (LPARAM) str);
|
||||
}
|
||||
if (wParam == VK_F1)
|
||||
{
|
||||
if (wParam == VK_F1) {
|
||||
MessageBoxA(hWnd, "Работу выполнил студент группы ПВ-221 Колесников А.И.", "О программе", MB_ICONINFORMATION);
|
||||
}
|
||||
break;
|
||||
|
||||
// Обработка сообщения на изменение размера окна
|
||||
// Обработка сообщения на изменение размера окна
|
||||
case WM_SIZE:
|
||||
|
||||
// Подгоняем размеры StatusBar под размер окна
|
||||
SendMessageA(hWndStatusBar, WM_SIZE, 0, 0);
|
||||
|
||||
// Перерисовать окно
|
||||
InvalidateRect(hWnd, NULL, false);
|
||||
InvalidateRect(hWnd, nullptr, false);
|
||||
break;
|
||||
|
||||
case WM_TIMER:
|
||||
|
||||
// При срабатывании таймера увеличим угол поворота
|
||||
global_angle += 0.05;
|
||||
// Перерисовать окно
|
||||
InvalidateRect(hWnd, NULL, false);
|
||||
reinterpret_cast<Painter *>(GetWindowLongPtr(hWnd, 0))->AngleStep();
|
||||
InvalidateRect(hWnd, nullptr, false);
|
||||
break;
|
||||
|
||||
case WM_DESTROY: // Завершение программы
|
||||
|
||||
case WM_DESTROY:
|
||||
delete reinterpret_cast<Painter *>(GetWindowLongPtr(hWnd, 0));
|
||||
SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(nullptr));
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Все сообщения, не обрабатываемые в данной функции, направляются на обработку по умолчанию
|
||||
return DefWindowProcA(hWnd, message, wParam, lParam);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
59
src/painter.cpp
Normal file
59
src/painter.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
#include "frame.hpp"
|
||||
#include "painter.hpp"
|
||||
|
||||
|
||||
void Painter::Draw(Frame &frame) {
|
||||
// Шахматная текстура
|
||||
for (int y = 0; y < frame.height; y++)
|
||||
for (int x = 0; x < frame.width; x++) {
|
||||
if ((x + y) % 2 == 0)
|
||||
frame.SetPixel(x, y, {230, 255, 230}); // Золотистый цвет
|
||||
//frame.SetPixel(x, y, { 217, 168, 14 });
|
||||
else
|
||||
frame.SetPixel(x, y, {200, 200, 200}); // Чёрный цвет
|
||||
//frame.SetPixel(x, y, { 255, 255, 255 }); // Белый цвет
|
||||
}
|
||||
|
||||
|
||||
int W = frame.width, H = frame.height;
|
||||
// Размер рисунка возьмём меньше (7 / 8), чтобы он не касался границ экрана
|
||||
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 {
|
||||
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.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));
|
||||
}
|
||||
|
||||
// Рисуем описанную окружность
|
||||
frame.Circle((int) C.x, (int) C.y, int(a * sqrt(2) + 0.5f), COLOR(100, 100, 250));
|
||||
|
||||
// Рисуем пиксель, на который кликнул пользователь
|
||||
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}); // Пиксель зелёного цвета
|
||||
}
|
27
src/painter.hpp
Normal file
27
src/painter.hpp
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "frame.hpp"
|
||||
|
||||
|
||||
class Painter {
|
||||
private:
|
||||
float global_angle = 0;
|
||||
|
||||
struct {
|
||||
int X, Y;
|
||||
} global_clicked_pixel = {-1, -1};
|
||||
public:
|
||||
|
||||
|
||||
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);
|
||||
};
|
150
src/shaders.cpp
150
src/shaders.cpp
@ -1,21 +1,18 @@
|
||||
#include "Painter.h"
|
||||
#include "frame.hpp"
|
||||
#include "painter.hpp"
|
||||
|
||||
template <class ShaderClass>
|
||||
void Triangle(float x0, float y0, float x1, float y1, float x2, float y2, COLOR color, ShaderClass &shader)
|
||||
{
|
||||
template<class ShaderClass>
|
||||
void Triangle(Frame *f, float x0, float y0, float x1, float y1, float x2, float y2, COLOR color, ShaderClass *shader) {
|
||||
// Отсортируем точки таким образом, чтобы выполнилось условие: y0 < y1 < y2
|
||||
if (y1 < y0)
|
||||
{
|
||||
if (y1 < y0) {
|
||||
swap(y1, y0);
|
||||
swap(x1, x0);
|
||||
}
|
||||
if (y2 < y1)
|
||||
{
|
||||
if (y2 < y1) {
|
||||
swap(y2, y1);
|
||||
swap(x2, x1);
|
||||
}
|
||||
if (y1 < y0)
|
||||
{
|
||||
if (y1 < y0) {
|
||||
swap(y1, y0);
|
||||
swap(x1, x0);
|
||||
}
|
||||
@ -25,136 +22,105 @@ void Triangle(float x0, float y0, float x1, float y1, float x2, float y2, COLOR
|
||||
int Y2 = (int) (y2 + 0.5f);
|
||||
// Отсечение невидимой части треугольника
|
||||
if (Y0 < 0) Y0 = 0;
|
||||
else if (Y0 >= height) Y0 = height;
|
||||
else if (Y0 >= f->height) Y0 = f->height;
|
||||
if (Y1 < 0) Y1 = 0;
|
||||
else if (Y1 >= height) Y1 = height;
|
||||
else if (Y1 >= f->height) Y1 = f->height;
|
||||
if (Y2 < 0) Y2 = 0;
|
||||
else if (Y2 >= height) Y2 = height;
|
||||
else if (Y2 >= f->height) Y2 = f->height;
|
||||
// Рисование верхней части треугольника
|
||||
for (float y = Y0 + 0.5f; y < Y1; y++)
|
||||
{
|
||||
for (float y = Y0 + 0.5f; y < Y1; y++) {
|
||||
int X0 = (int) ((y - y0) / (y1 - y0) * (x1 - x0) + x0 + 0.5f);
|
||||
int X1 = (int) ((y - y0) / (y2 - y0) * (x2 - x0) + x0 + 0.5f);
|
||||
if (X0 > X1) swap(X0, X1);
|
||||
if (X0 < 0) X0 = 0;
|
||||
if (X1 > width) X1 = width;
|
||||
for (int x = X0; x < X1; x++)
|
||||
{
|
||||
if (X1 > f->width) X1 = f->width;
|
||||
for (int x = X0; x < X1; x++) {
|
||||
// f(x + 0.5, y)
|
||||
SetPixel(x, y, shader.getColor(x + 0.5f, y));
|
||||
SetPixel(x, y, shader->getColor(x + 0.5f, y));
|
||||
}
|
||||
}
|
||||
// Рисование нижней части треугольника
|
||||
for (float y = Y1 + 0.5f; y < Y2; y++)
|
||||
{
|
||||
int X0 = (int)((y - y1) / (y2 - y1) * (x2 - x1) + x1 + 0.5f);
|
||||
int X1 = (int)((y - y0) / (y2 - y0) * (x2 - x0) + x0 + 0.5f);
|
||||
for (float y = Y1 + 0.5f; y < Y2; y++) {
|
||||
int X0 = (int) ((y - y1) / (y2 - y1) * (x2 - x1) + x1 + 0.5f);
|
||||
int X1 = (int) ((y - y0) / (y2 - y0) * (x2 - x0) + x0 + 0.5f);
|
||||
if (X0 > X1) swap(X0, X1);
|
||||
if (X0 < 0) X0 = 0;
|
||||
if (X1 > width) X1 = width;
|
||||
for (int x = X0; x < X1; x++)
|
||||
{
|
||||
if (X1 > f->width) X1 = f->width;
|
||||
for (int x = X0; x < X1; x++) {
|
||||
// f(x + 0.5, y)
|
||||
SetPixel(x, y, shader.getColor(x + 0.5f, y));
|
||||
SetPixel(x, y, shader->getColor(x + 0.5f, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BarycentricInterpolator
|
||||
{
|
||||
class BarycentricInterpolator {
|
||||
float x0, y0, x1, y1, x2, y2, S;
|
||||
COLOR C0, C1, C2;
|
||||
public:
|
||||
BarycentricInterpolator(float _x0, float _y0, float _x1, float _y1, float
|
||||
_x2, float _y2, COLOR A0, COLOR A1, COLOR A2) :
|
||||
x0(_x0), y0(_y0), x1(_x1), y1(_y1), x2(_x2), y2(_y2),
|
||||
S((_y1 - _y2)*(_x0 - _x2) + (_x2 - _x1)*(_y0 - _y2)), C0(A0), C1(A1),
|
||||
C2(A2)
|
||||
{
|
||||
S((_y1 - _y2) * (_x0 - _x2) + (_x2 - _x1) * (_y0 - _y2)), C0(A0), C1(A1),
|
||||
C2(A2) {
|
||||
}
|
||||
COLOR getColor(float x, float y)
|
||||
{
|
||||
|
||||
COLOR getColor(float x, float y) {
|
||||
// Барицентрическая интерполяция
|
||||
float h0 = ((y1 - y2) * (x - x2) + (x2 - x1) * (y - y2)) / S;
|
||||
float h1 = ((y2 - y0) * (x - x2) + (x0 - x2) * (y - y2)) / S;
|
||||
float h2 = 1 - h0 - h1;
|
||||
float r = h0 * C0.RED + h1 * C1.RED + h2 * C2.RED;
|
||||
float g = h0 * C0.GREEN + h1 * C1.GREEN + h2 * C2.GREEN;
|
||||
float b = h0 * C0.BLUE + h1 * C1.BLUE + h2 * C2.BLUE;
|
||||
float a = h0 * C0.ALPHA + h1 * C1.ALPHA + h2 * C2.ALPHA;
|
||||
float r = h0 * C0.red + h1 * C1.red + h2 * C2.red;
|
||||
float g = h0 * C0.green + h1 * C1.green + h2 * C2.green;
|
||||
float b = h0 * C0.blue + h1 * C1.blue + h2 * C2.blue;
|
||||
float a = h0 * C0.alpha + h1 * C1.alpha + h2 * C2.alpha;
|
||||
return COLOR(r, g, b, a);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class RadialBrush
|
||||
{
|
||||
class RadialBrush {
|
||||
float cx, cy; // Центр прямоугольника
|
||||
COLOR C0, C1; // Цвета радиальной заливки
|
||||
float angle; // Начальный угол заливки
|
||||
public:
|
||||
RadialBrush (float _x0, float _y0, float _x1, float _y1, COLOR A0, COLOR A1, float
|
||||
RadialBrush(float _x0, float _y0, float _x1, float _y1, COLOR A0, COLOR A1, float
|
||||
_angle) :
|
||||
cx((_x0 + _x1) / 2.0f), cy((_y0 + _y1)/ 2.0f),
|
||||
C0(A0), C1(A1), angle(_angle)
|
||||
{
|
||||
cx((_x0 + _x1) / 2.0f), cy((_y0 + _y1) / 2.0f),
|
||||
C0(A0), C1(A1), angle(_angle) {
|
||||
}
|
||||
COLOR getColor(float x, float y)
|
||||
{
|
||||
double dx = (double)x - cx, dy = (double)y - cy;
|
||||
double radius = sqrt(dx*dx + dy*dy);
|
||||
|
||||
COLOR getColor(float x, float y) {
|
||||
double dx = (double) x - cx, dy = (double) y - cy;
|
||||
double radius = sqrt(dx * dx + dy * dy);
|
||||
float h0 = (sin(radius / 10 + angle) + 1.0f) / 2;
|
||||
float h1 = 1 - h0;
|
||||
float r = h0 * C0.RED + h1 * C1.RED;
|
||||
float g = h0 * C0.GREEN + h1 * C1.GREEN;
|
||||
float b = h0 * C0.BLUE + h1 * C1.BLUE;
|
||||
float r = h0 * C0.red + h1 * C1.red;
|
||||
float g = h0 * C0.green + h1 * C1.green;
|
||||
float b = h0 * C0.blue + h1 * C1.blue;
|
||||
return COLOR(r, g, b);
|
||||
}
|
||||
};
|
||||
|
||||
void ColorFromHSV(double hue, double saturation, double value, GLint *color)
|
||||
{
|
||||
COLOR ColorFromHSV(double hue, double saturation, double value) {
|
||||
int hi = int(floor(hue / 60)) % 6;
|
||||
double f = hue / 60 - floor(hue / 60);
|
||||
value = value * 255;
|
||||
int v = (int)(value);
|
||||
int p = (int)(value * (1 - saturation));
|
||||
int q = (int)(value * (1 - f * saturation));
|
||||
int t = (int)(value * (1 - (1 - f) * saturation));
|
||||
if (hi == 0)
|
||||
{
|
||||
color[0] = v;
|
||||
color[1] = t;
|
||||
color[2] = p;
|
||||
}
|
||||
else if (hi == 1)
|
||||
{
|
||||
color[0] = q;
|
||||
color[1] = v;
|
||||
color[2] = p;
|
||||
}
|
||||
else if (hi == 2)
|
||||
{
|
||||
color[0] = p;
|
||||
color[1] = v;
|
||||
color[2] = t;
|
||||
}
|
||||
else if (hi == 3)
|
||||
{
|
||||
color[0] = p;
|
||||
color[1] = q;
|
||||
color[2] = v;
|
||||
}
|
||||
else if (hi == 4)
|
||||
{
|
||||
color[0] = t;
|
||||
color[1] = p;
|
||||
color[2] = v;
|
||||
}
|
||||
else
|
||||
{
|
||||
color[0] = v;
|
||||
color[1] = p;
|
||||
color[2] = q;
|
||||
const auto v = (unsigned char) (value);
|
||||
const auto p = (unsigned char) (value * (1 - saturation));
|
||||
const auto q = (unsigned char) (value * (1 - f * saturation));
|
||||
const auto t = (unsigned char) (value * (1 - (1 - f) * saturation));
|
||||
switch (hi) {
|
||||
case 0:
|
||||
return COLOR{v, t, p};
|
||||
case 1:
|
||||
return COLOR{q, v, p};
|
||||
case 2:
|
||||
return COLOR{p, v, t};
|
||||
case 3:
|
||||
return COLOR{p, q, v};
|
||||
case 4:
|
||||
return COLOR{t, p, v};
|
||||
default:
|
||||
return COLOR{v, p, q};
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user