Загрузка данных
#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;
}