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


#include <iostream>
#include <fstream>
#include <vector>
#include <windows.h>

// Структура для хранения пикселя
struct Pixel {
    unsigned char b, g, r;
    
    Pixel() : b(0), g(0), r(0) {}
    Pixel(unsigned char red, unsigned char green, unsigned char blue) 
        : b(blue), g(green), r(red) {}
    
    bool operator==(const Pixel& other) const {
        return r == other.r && g == other.g && b == other.b;
    }
};

// Структура для RLE записи
struct RLERecord {
    Pixel color;
    int count;
    
    RLERecord(const Pixel& p, int c) : color(p), count(c) {}
};

// Структуры BMP файла
#pragma pack(push, 1)
struct BMPFileHeader {
    unsigned short bfType;      // 'BM'
    unsigned int bfSize;        // Размер файла
    unsigned short bfReserved1; // 0
    unsigned short bfReserved2; // 0
    unsigned int bfOffBits;     // Смещение до пикселей
};

struct BMPInfoHeader {
    unsigned int biSize;          // Размер заголовка (40)
    int biWidth;                  // Ширина
    int biHeight;                 // Высота
    unsigned short biPlanes;      // 1
    unsigned short biBitCount;    // 24 бита
    unsigned int biCompression;   // 0 (без сжатия)
    unsigned int biSizeImage;     // Размер изображения
    int biXPelsPerMeter;          // 0
    int biYPelsPerMeter;          // 0
    unsigned int biClrUsed;       // 0
    unsigned int biClrImportant;  // 0
};
#pragma pack(pop)

// Класс для работы с изображением
class PikachuImage {
private:
    int width;
    int height;
    std::vector<std::vector<Pixel>> pixels;
    
public:
    PikachuImage(int w, int h) : width(w), height(h) {
        pixels.resize(height, std::vector<Pixel>(width));
    }
    
    // Получить пиксель
    Pixel& getPixel(int x, int y) {
        return pixels[y][x];
    }
    
    // Нарисовать Пикачу
    void drawPikachu() {
        // Цвета
        Pixel yellow(255, 220, 60);    // Желтый
        Pixel black(0, 0, 0);           // Черный
        Pixel red(255, 80, 80);         // Красный (щеки)
        Pixel pink(255, 150, 150);      // Розовый (рот)
        Pixel white(255, 255, 255);     // Белый (глаза)
        Pixel brown(139, 90, 43);       // Коричневый
        
        // Очистка белым фоном
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                pixels[y][x] = white;
            }
        }
        
        // Рисуем Пикачу (примерная схема для 128x128)
        // Уши (черные кончики)
        drawEar(20, 20, 45, 50, black, yellow);
        drawEar(83, 20, 108, 50, black, yellow);
        
        // Голова (желтая)
        drawEllipse(35, 45, 93, 95, yellow);
        
        // Глаза (черные с белыми бликами)
        drawCircle(50, 65, 12, black);
        drawCircle(78, 65, 12, black);
        drawCircle(53, 62, 4, white);
        drawCircle(81, 62, 4, white);
        
        // Щеки (красные круги)
        drawCircle(40, 80, 10, red);
        drawCircle(88, 80, 10, red);
        
        // Нос (маленькая черная точка)
        drawCircle(64, 73, 2, black);
        
        // Рот (розовый)
        drawMouth(55, 82, 73, 90, pink, black);
    }
    
    void drawEar(int x1, int y1, int x2, int y2, Pixel colorBlack, Pixel colorYellow) {
        for (int y = y1; y <= y2; y++) {
            for (int x = x1; x <= x2; x++) {
                if (y < y1 + 15) {
                    // Верхняя часть - черная
                    if (isValid(x, y)) pixels[y][x] = colorBlack;
                } else {
                    // Нижняя часть - желтая
                    if (isValid(x, y)) pixels[y][x] = colorYellow;
                }
            }
        }
    }
    
    void drawEllipse(int x1, int y1, int x2, int y2, Pixel color) {
        int centerX = (x1 + x2) / 2;
        int centerY = (y1 + y2) / 2;
        int radiusX = (x2 - x1) / 2;
        int radiusY = (y2 - y1) / 2;
        
        for (int y = y1; y <= y2; y++) {
            for (int x = x1; x <= x2; x++) {
                double nx = (double)(x - centerX) / radiusX;
                double ny = (double)(y - centerY) / radiusY;
                
                if (nx * nx + ny * ny <= 1.0) {
                    if (isValid(x, y)) {
                        pixels[y][x] = color;
                    }
                }
            }
        }
    }
    
    void drawCircle(int cx, int cy, int radius, Pixel color) {
        for (int y = cy - radius; y <= cy + radius; y++) {
            for (int x = cx - radius; x <= cx + radius; x++) {
                int dx = x - cx;
                int dy = y - cy;
                
                if (dx * dx + dy * dy <= radius * radius) {
                    if (isValid(x, y)) {
                        pixels[y][x] = color;
                    }
                }
            }
        }
    }
    
    void drawMouth(int x1, int y1, int x2, int y2, Pixel colorPink, Pixel colorBlack) {
        // Рисуем улыбку
        for (int x = x1; x <= x2; x++) {
            int y = y1 + abs(x - (x1 + x2) / 2) / 2;
            if (isValid(x, y)) {
                pixels[y][x] = colorPink;
            }
            // Контур
            if (isValid(x, y - 1)) {
                pixels[y - 1][x] = colorBlack;
            }
        }
    }
    
    bool isValid(int x, int y) {
        return x >= 0 && x < width && y >= 0 && y < height;
    }
    
    // RLE сжатие согласно блок-схеме
    std::vector<std::vector<RLERecord>> compressRLE() {
        std::vector<std::vector<RLERecord>> result(height);
        
        // Для каждой строки (i)
        for (int i = 0; i < height; i++) {
            std::vector<RLERecord>& rowRecords = result[i];
            
            // Инициализация первого пикселя
            Pixel current_r = pixels[i][0];
            int current_count = 0;
            
            // Проход по всем пикселям строки (j)
            for (int j = 0; j < width; j++) {
                Pixel temp_r = pixels[i][j];
                
                // Проверка: current_r == temp_r (по блок-схеме)
                if (current_r == temp_r) {
                    current_count += 1;
                } else {
                    // Добавляем запись в результат
                    rowRecords.push_back(RLERecord(current_r, current_count));
                    
                    // Обновляем текущий пиксель
                    current_r = temp_r;
                    current_count = 1;
                }
            }
            
            // Добавляем последнюю последовательность
            rowRecords.push_back(RLERecord(current_r, current_count));
        }
        
        return result;
    }
    
    // Сохранение в BMP файл
    bool saveBMP(const std::string& filename) {
        std::ofstream file(filename, std::ios::binary);
        if (!file) {
            std::cerr << "Не удалось создать файл!" << std::endl;
            return false;
        }
        
        // Размер строки с выравниванием до 4 байт
        int rowSize = ((width * 3 + 3) / 4) * 4;
        int imageSize = rowSize * height;
        
        // Заголовки
        BMPFileHeader fileHeader;
        BMPInfoHeader infoHeader;
        
        fileHeader.bfType = 0x4D42; // 'BM'
        fileHeader.bfSize = sizeof(BMPFileHeader) + sizeof(BMPInfoHeader) + imageSize;
        fileHeader.bfReserved1 = 0;
        fileHeader.bfReserved2 = 0;
        fileHeader.bfOffBits = sizeof(BMPFileHeader) + sizeof(BMPInfoHeader);
        
        infoHeader.biSize = sizeof(BMPInfoHeader);
        infoHeader.biWidth = width;
        infoHeader.biHeight = height;
        infoHeader.biPlanes = 1;
        infoHeader.biBitCount = 24;
        infoHeader.biCompression = 0;
        infoHeader.biSizeImage = imageSize;
        infoHeader.biXPelsPerMeter = 0;
        infoHeader.biYPelsPerMeter = 0;
        infoHeader.biClrUsed = 0;
        infoHeader.biClrImportant = 0;
        
        // Запись заголовков
        file.write(reinterpret_cast<char*>(&fileHeader), sizeof(fileHeader));
        file.write(reinterpret_cast<char*>(&infoHeader), sizeof(infoHeader));
        
        // Запись пикселей (BMP хранится снизу вверх)
        std::vector<unsigned char> rowBuffer(rowSize, 0);
        
        for (int y = height - 1; y >= 0; y--) {
            for (int x = 0; x < width; x++) {
                int pos = x * 3;
                rowBuffer[pos] = pixels[y][x].b;
                rowBuffer[pos + 1] = pixels[y][x].g;
                rowBuffer[pos + 2] = pixels[y][x].r;
            }
            file.write(reinterpret_cast<char*>(rowBuffer.data()), rowSize);
        }
        
        file.close();
        return true;
    }
    
    int getWidth() const { return width; }
    int getHeight() const { return height; }
};

// Вывод статистики RLE
void printRLEStats(const std::vector<std::vector<RLERecord>>& rleData) {
    int totalRecords = 0;
    int totalPixels = 0;
    
    for (const auto& row : rleData) {
        totalRecords += row.size();
        for (const auto& record : row) {
            totalPixels += record.count;
        }
    }
    
    std::cout << "\n=== СТАТИСТИКА RLE СЖАТИЯ ===" << std::endl;
    std::cout << "Всего пикселей: " << totalPixels << std::endl;
    std::cout << "Всего RLE записей: " << totalRecords << std::endl;
    std::cout << "Средняя длина серии: " << (double)totalPixels / totalRecords << std::endl;
    std::cout << "Коэффициент сжатия: " << (double)totalRecords / totalPixels * 100 << "%" << std::endl;
}

// Вывод первых нескольких записей RLE для примера
void printRLEExample(const std::vector<std::vector<RLERecord>>& rleData, int rows = 5) {
    std::cout << "\n=== ПРИМЕР RLE ЗАПИСЕЙ (первые " << rows << " строк) ===" << std::endl;
    
    for (int i = 0; i < std::min((int)rleData.size(), rows); i++) {
        std::cout << "\nСтрока " << i << ": ";
        int count = 0;
        for (const auto& record : rleData[i]) {
            if (count++ > 10) {
                std::cout << "... ";
                break;
            }
            std::cout << "[" << (int)record.color.r << "," 
                      << (int)record.color.g << "," 
                      << (int)record.color.b << "x" 
                      << record.count << "] ";
        }
    }
}

int main() {
    setlocale(LC_ALL, "Russian");
    
    std::cout << "=== ГЕНЕРАТОР PIKACHU С RLE СЖАТИЕМ ===" << std::endl;
    
    // Параметры изображения
    int width = 128;
    int height = 128;
    
    std::cout << "\nСоздание изображения " << width << "x" << height << "..." << std::endl;
    
    // Создание изображения
    PikachuImage image(width, height);
    
    // Рисование Пикачу
    std::cout << "Рисование Пикачу..." << std::endl;
    image.drawPikachu();
    
    // RLE сжатие (согласно блок-схеме)
    std::cout << "Применение RLE сжатия..." << std::endl;
    auto rleData = image.compressRLE();
    
    // Вывод статистики
    printRLEStats(rleData);
    printRLEExample(rleData);
    
    // Сохранение изображения
    std::string filename = "pikachu.bmp";
    std::cout << "\nСохранение в файл: " << filename << std::endl;
    
    if (image.saveBMP(filename)) {
        std::cout << "✓ Изображение успешно сохранено!" << std::endl;
        std::cout << "Откройте файл " << filename << " в Paint или другом просмотрщике." << std::endl;
    } else {
        std::cout << "✗ Ошибка при сохранении!" << std::endl;
    }
    
    std::cout << "\nНажмите Enter для выхода..." << std::endl;
    std::cin.get();
    
    return 0;
}