Загрузка данных


#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;
}