Загрузка данных
#pragma once
#include <SFML/Graphics.hpp>
#include <vector>
const int WIDTH = 800;
const int HEIGHT = 600;
const int PIXEL_SIZE = 1;
using Canvas = std::vector<std::vector<sf::Color>>;
// Drawing
void clearCanvas(Canvas& canvas);
void drawPixel(Canvas& canvas, int x, int y, const sf::Color& color);
void drawBrush(Canvas& canvas, int x, int y,
const sf::Color& color, int radius);
// Shapes
void drawRectangle(Canvas& canvas,
int x1, int y1,
int x2, int y2,
const sf::Color& color);
void drawLine(Canvas& canvas,
int x1, int y1,
int x2, int y2,
const sf::Color& color);
// Undo
void undo(Canvas& canvas);
void saveToUndo(const Canvas& canvas);
// Filters
void applyGrayscale(Canvas& canvas);
void applyNegative(Canvas& canvas);
void applyBlur(Canvas& canvas);
// Render
void renderToWindow(sf::RenderWindow& window,
const Canvas& canvas);
123
#include "paint_core.hpp"
#include <algorithm>
#include <cmath>
#include <stack>
extern std::stack<Canvas> undoStack;
void saveToUndo(const Canvas& canvas) {
undoStack.push(canvas);
}
void clearCanvas(Canvas& canvas) {
for (auto& row : canvas) {
std::fill(row.begin(), row.end(), sf::Color::White);
}
}
void drawPixel(Canvas& canvas,
int x, int y,
const sf::Color& color) {
if (x >= 0 && x < WIDTH &&
y >= 0 && y < HEIGHT) {
canvas[y][x] = color;
}
}
void drawBrush(Canvas& canvas,
int x, int y,
const sf::Color& color,
int radius) {
for (int dy = -radius; dy <= radius; ++dy) {
for (int dx = -radius; dx <= radius; ++dx) {
if (dx * dx + dy * dy <= radius * radius) {
drawPixel(canvas, x + dx, y + dy, color);
}
}
}
}
void drawRectangle(Canvas& canvas,
int x1, int y1,
int x2, int y2,
const sf::Color& color) {
int left = std::min(x1, x2);
int right = std::max(x1, x2);
int top = std::min(y1, y2);
int bottom = std::max(y1, y2);
for (int y = top; y <= bottom; ++y) {
for (int x = left; x <= right; ++x) {
drawPixel(canvas, x, y, color);
}
}
}
void drawLine(Canvas& canvas,
int x1, int y1,
int x2, int y2,
const sf::Color& color) {
int dx = std::abs(x2 - x1);
int dy = std::abs(y2 - y1);
int sx = (x1 < x2) ? 1 : -1;
int sy = (y1 < y2) ? 1 : -1;
int err = dx - dy;
while (true) {
drawPixel(canvas, x1, y1, color);
if (x1 == x2 && y1 == y2)
break;
int e2 = 2 * err;
if (e2 > -dy) {
err -= dy;
x1 += sx;
}
if (e2 < dx) {
err += dx;
y1 += sy;
}
}
}
void undo(Canvas& canvas) {
if (!undoStack.empty()) {
canvas = undoStack.top();
undoStack.pop();
}
}
void applyGrayscale(Canvas& canvas) {
saveToUndo(canvas);
for (auto& row : canvas) {
std::transform(
row.begin(),
row.end(),
row.begin(),
[](const sf::Color& c) {
int gray = (c.r + c.g + c.b) / 3;
return sf::Color(gray, gray, gray);
}
);
}
}
void applyNegative(Canvas& canvas) {
saveToUndo(canvas);
for (auto& row : canvas) {
std::transform(
row.begin(),
row.end(),
row.begin(),
[](const sf::Color& c) {
return sf::Color(
255 - c.r,
255 - c.g,
255 - c.b
);
}
);
}
}
void applyBlur(Canvas& canvas) {
saveToUndo(canvas);
Canvas original = canvas;
for (int y = 1; y < HEIGHT - 1; ++y) {
for (int x = 1; x < WIDTH - 1; ++x) {
int sumR = 0;
int sumG = 0;
int sumB = 0;
for (int dy = -1; dy <= 1; ++dy) {
for (int dx = -1; dx <= 1; ++dx) {
sf::Color c =
original[y + dy][x + dx];
sumR += c.r;
sumG += c.g;
sumB += c.b;
}
}
canvas[y][x] = sf::Color(
sumR / 9,
sumG / 9,
sumB / 9
);
}
}
}
void renderToWindow(sf::RenderWindow& window,
const Canvas& canvas) {
static sf::Image image;
static sf::Texture texture;
static sf::Sprite sprite;
image.create(WIDTH, HEIGHT);
for (int y = 0; y < HEIGHT; ++y) {
for (int x = 0; x < WIDTH; ++x) {
image.setPixel(x, y, canvas[y][x]);
}
}
texture.loadFromImage(image);
texture.setSmooth(false);
sprite.setTexture(texture);
sprite.setScale(PIXEL_SIZE, PIXEL_SIZE);
window.draw(sprite);
}
123
#include "paint_core.hpp"
#include <iostream>
#include <vector>
#include <string>
#include <stack>
std::stack<Canvas> undoStack;
enum class Tool { BRUSH, RECTANGLE, LINE, ERASER };
int main() {
const int UI_HEIGHT = 120;
sf::RenderWindow window(
sf::VideoMode(WIDTH * PIXEL_SIZE, HEIGHT * PIXEL_SIZE + UI_HEIGHT),
"Paint++"
);
auto desktop = sf::VideoMode::getDesktopMode();
window.setPosition(sf::Vector2i(
(desktop.width - window.getSize().x) / 2,
(desktop.height - window.getSize().y) / 2
));
Canvas canvas(HEIGHT, std::vector<sf::Color>(WIDTH, sf::Color::White));
clearCanvas(canvas);
Tool currentTool = Tool::RECTANGLE;
sf::Color currentColor = sf::Color::Black;
int brushRadius = 3;
std::vector<sf::Color> palette = {
sf::Color::Black,
sf::Color::White,
sf::Color::Red,
sf::Color::Green,
sf::Color::Blue,
sf::Color::Yellow,
sf::Color::Cyan,
sf::Color::Magenta,
sf::Color(255, 128, 0),
sf::Color(128, 0, 128)
};
int selectedPaletteIndex = 0;
bool isDrawing = false;
int startX = 0, startY = 0;
int lastX = 0, lastY = 0;
sf::Font font;
font.loadFromFile("arial.ttf");
sf::Text status;
status.setFont(font);
status.setCharacterSize(14);
status.setFillColor(sf::Color::Black);
status.setPosition(10, HEIGHT * PIXEL_SIZE + 10);
std::cout << "=== PAINT with STL ===\n"
<< "Tools:\n"
<< "B - Brush\n"
<< "R - Rectangle\n"
<< "L - Line\n"
<< "E - Eraser\n"
<< "+/- - Brush size\n"
<< "C - Clear canvas\n"
<< "Ctrl+Z - Undo\n"
<< "G - Grayscale\n"
<< "N - Negative\n"
<< "M - Blur\n";
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed)
window.close();
if (event.type == sf::Event::KeyPressed) {
switch (event.key.code) {
case sf::Keyboard::B:
currentTool = Tool::BRUSH;
break;
case sf::Keyboard::R:
currentTool = Tool::RECTANGLE;
break;
case sf::Keyboard::L:
currentTool = Tool::LINE;
break;
case sf::Keyboard::E:
currentTool = Tool::ERASER;
break;
case sf::Keyboard::C:
clearCanvas(canvas);
break;
case sf::Keyboard::Z:
if (event.key.control)
undo(canvas);
break;
case sf::Keyboard::G:
applyGrayscale(canvas);
break;
case sf::Keyboard::N:
applyNegative(canvas);
break;
case sf::Keyboard::M:
applyBlur(canvas);
break;
case sf::Keyboard::Add:
case sf::Keyboard::Equal:
if (brushRadius < 20)
brushRadius++;
break;
case sf::Keyboard::Hyphen:
case sf::Keyboard::Subtract:
if (brushRadius > 1)
brushRadius--;
break;
case sf::Keyboard::Num0:
case sf::Keyboard::Num1:
case sf::Keyboard::Num2:
case sf::Keyboard::Num3:
case sf::Keyboard::Num4:
case sf::Keyboard::Num5:
case sf::Keyboard::Num6:
case sf::Keyboard::Num7:
case sf::Keyboard::Num8:
case sf::Keyboard::Num9: {
int idx = event.key.code - sf::Keyboard::Num0;
if (idx >= 0 && idx < palette.size()) {
selectedPaletteIndex = idx;
currentColor = palette[idx];
}
break;
}
default:
break;
}
}
if (event.type == sf::Event::MouseButtonPressed &&
event.mouseButton.button == sf::Mouse::Left) {
sf::Vector2i mouse = sf::Mouse::getPosition(window);
int paletteY = HEIGHT * PIXEL_SIZE + 50;
if (mouse.y >= paletteY && mouse.y <= paletteY + 30) {
for (size_t i = 0; i < palette.size(); ++i) {
int boxX = 10 + i * 35;
if (mouse.x >= boxX && mouse.x <= boxX + 30) {
selectedPaletteIndex = i;
currentColor = palette[i];
}
}
}
isDrawing = true;
startX = mouse.x / PIXEL_SIZE;
startY = mouse.y / PIXEL_SIZE;
lastX = startX;
lastY = startY;
if (currentTool == Tool::RECTANGLE ||
currentTool == Tool::LINE) {
saveToUndo(canvas);
} else {
saveToUndo(canvas);
sf::Color drawColor =
(currentTool == Tool::ERASER)
? sf::Color::White
: currentColor;
drawBrush(canvas, startX, startY,
drawColor, brushRadius);
}
}
if (event.type == sf::Event::MouseButtonReleased &&
event.mouseButton.button == sf::Mouse::Left) {
if (isDrawing &&
(currentTool == Tool::RECTANGLE ||
currentTool == Tool::LINE)) {
sf::Vector2i mouse =
sf::Mouse::getPosition(window);
int endX = mouse.x / PIXEL_SIZE;
int endY = mouse.y / PIXEL_SIZE;
sf::Color drawColor =
(currentTool == Tool::ERASER)
? sf::Color::White
: currentColor;
if (currentTool == Tool::RECTANGLE)
drawRectangle(canvas,
startX, startY,
endX, endY,
drawColor);
else if (currentTool == Tool::LINE)
drawLine(canvas,
startX, startY,
endX, endY,
drawColor);
}
isDrawing = false;
}
if (event.type == sf::Event::MouseMoved && isDrawing) {
sf::Vector2i mouse =
sf::Mouse::getPosition(window);
int x = mouse.x / PIXEL_SIZE;
int y = mouse.y / PIXEL_SIZE;
if ((currentTool == Tool::BRUSH ||
currentTool == Tool::ERASER) &&
(x != lastX || y != lastY)) {
sf::Color drawColor =
(currentTool == Tool::ERASER)
? sf::Color::White
: currentColor;
drawBrush(canvas, x, y,
drawColor, brushRadius);
lastX = x;
lastY = y;
}
}
}
window.clear(sf::Color(200, 200, 200));
renderToWindow(window, canvas);
for (size_t i = 0; i < palette.size(); ++i) {
sf::RectangleShape box(sf::Vector2f(30, 30));
box.setFillColor(palette[i]);
box.setPosition(
10 + i * 35,
HEIGHT * PIXEL_SIZE + 50
);
box.setOutlineThickness(2);
box.setOutlineColor(
(i == selectedPaletteIndex)
? sf::Color::Red
: sf::Color::Black
);
window.draw(box);
}
std::string toolStr;
switch (currentTool) {
case Tool::BRUSH:
toolStr = "BRUSH";
break;
case Tool::RECTANGLE:
toolStr = "RECTANGLE";
break;
case Tool::LINE:
toolStr = "LINE";
break;
case Tool::ERASER:
toolStr = "ERASER";
break;
}
status.setString(
toolStr +
" | Radius: " + std::to_string(brushRadius) +
" | Undo: " + std::to_string(undoStack.size())
);
window.draw(status);
window.display();
}
return 0;
}