Загрузка данных
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <algorithm>
#include <cmath>
#include <windows.h>
// Структура для хранения пикселя (RGB)
struct Pixel {
unsigned char r;
unsigned char g;
unsigned char b;
Pixel() : r(0), g(0), b(0) {}
Pixel(unsigned char red, unsigned char green, unsigned char blue)
: r(red), g(green), b(blue) {}
bool operator==(const Pixel& other) const {
return (r == other.r) && (g == other.g) && (b == other.b);
}
bool operator!=(const Pixel& other) const {
return !(*this == other);
}
};
// Структура для RLE записи
struct RLERecord {
Pixel color;
int count;
RLERecord() : count(0) {}
RLERecord(const Pixel& p, int c) : color(p), count(c) {}
};
// Заголовки BMP файла
#pragma pack(push, 1)
struct BMPFileHeader {
unsigned short bfType;
unsigned int bfSize;
unsigned short bfReserved1;
unsigned short bfReserved2;
unsigned int bfOffBits;
};
struct BMPInfoHeader {
unsigned int biSize;
int biWidth;
int biHeight;
unsigned short biPlanes;
unsigned short biBitCount;
unsigned int biCompression;
unsigned int biSizeImage;
int biXPelsPerMeter;
int biYPelsPerMeter;
unsigned int biClrUsed;
unsigned int biClrImportant;
};
#pragma pack(pop)
// Класс изображения Пикачу
class PikachuImage {
private:
int width;
int height;
std::vector<std::vector<Pixel>> pixels;
bool isValid(int x, int y) const {
return (x >= 0) && (x < width) && (y >= 0) && (y < height);
}
void setPixel(int x, int y, const Pixel& color) {
if (isValid(x, y)) {
pixels[y][x] = color;
}
}
double sign(int x1, int y1, int x2, int y2, int x3, int y3) {
return (double)(x1 - x3) * (y2 - y3) - (double)(x2 - x3) * (y1 - y3);
}
// Рисование заполненного эллипса
void drawEllipse(int x1, int y1, int x2, int y2, const Pixel& color) {
int centerX = (x1 + x2) / 2;
int centerY = (y1 + y2) / 2;
int radiusX = (x2 - x1) / 2;
int radiusY = (y2 - y1) / 2;
if (radiusX <= 0 || radiusY <= 0) return;
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) {
setPixel(x, y, color);
}
}
}
}
// Рисование заполненного круга
void drawCircle(int cx, int cy, int radius, const 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)) {
setPixel(x, y, color);
}
}
}
}
// Рисование треугольника (ухо)
void drawTriangle(int x1, int y1, int x2, int y2, int x3, int y3, const Pixel& color) {
int minY = std::min({y1, y2, y3});
int maxY = std::max({y1, y2, y3});
for (int y = minY; y <= maxY; y++) {
for (int x = 0; x < width; x++) {
double d1 = sign(x, y, x1, y1, x2, y2);
double d2 = sign(x, y, x2, y2, x3, y3);
double d3 = sign(x, y, x3, y3, x1, y1);
bool hasNeg = (d1 < 0) || (d2 < 0) || (d3 < 0);
bool hasPos = (d1 > 0) || (d2 > 0) || (d3 > 0);
if (!(hasNeg && hasPos)) {
setPixel(x, y, color);
}
}
}
}
public:
PikachuImage(int w, int h) : width(w), height(h) {
pixels.resize(height, std::vector<Pixel>(width));
}
// Получить пиксель
Pixel getPixel(int x, int y) const {
if (isValid(x, y)) return pixels[y][x];
return Pixel();
}
// Нарисовать Пикачу
void drawPikachu() {
// Определяем цвета
Pixel white(255, 255, 255);
Pixel yellow(252, 220, 48);
Pixel black(30, 30, 30);
Pixel darkYellow(200, 170, 30);
Pixel red(220, 50, 50);
Pixel brown(120, 80, 40);
Pixel pink(240, 150, 150);
Pixel whiteEye(255, 255, 255);
// Заполняем белым фоном
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
pixels[y][x] = white;
}
}
// Рисуем Пикачу по координатам (для 128x128)
// Левое ухо (треугольник)
drawTriangle(15, 55, 25, 15, 45, 50, yellow);
drawTriangle(15, 55, 20, 25, 35, 50, black);
// Правое ухо (треугольник)
drawTriangle(113, 55, 103, 15, 83, 50, yellow);
drawTriangle(113, 55, 108, 25, 93, 50, black);
// Голова (большой эллипс)
drawEllipse(25, 45, 103, 115, yellow);
// Контур головы
drawEllipse(25, 45, 103, 115, black);
drawEllipse(27, 47, 101, 113, yellow);
// Левый глаз (чёрный)
drawCircle(50, 70, 10, black);
drawCircle(47, 67, 4, whiteEye);
// Правый глаз (чёрный)
drawCircle(78, 70, 10, black);
drawCircle(75, 67, 4, whiteEye);
// Нос
drawCircle(64, 78, 3, black);
// Рот (улыбка)
for (int x = 55; x <= 73; x++) {
int y = 82 + abs(x - 64) / 3;
setPixel(x, y, black);
}
// Язык (розовый)
drawCircle(64, 92, 6, pink);
// Щёки (красные круги)
drawCircle(38, 85, 9, red);
drawCircle(90, 85, 9, red);
}
// RLE сжатие по блок-схеме
std::vector<std::vector<RLERecord>> compressRLE() const {
std::vector<std::vector<RLERecord>> result(height);
for (int i = 0; i < height; i++) {
std::vector<RLERecord> rowRecords;
if (width == 0) continue;
Pixel current_r = pixels[i][0];
int current_count = 1;
for (int j = 1; j < width; j++) {
Pixel temp_r = pixels[i][j];
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));
result[i] = rowRecords;
}
return result;
}
// Сохранение в BMP файл
bool saveBMP(const std::string& filename) const {
std::ofstream file(filename, std::ios::binary);
if (!file) {
std::cerr << "Не удалось создать файл: " << filename << std::endl;
return false;
}
int rowSize = ((width * 3 + 3) / 4) * 4;
int padding = rowSize - width * 3;
int imageSize = rowSize * height;
BMPFileHeader fileHeader;
BMPInfoHeader infoHeader;
fileHeader.bfType = 0x4D42;
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<const char*>(&fileHeader), sizeof(fileHeader));
file.write(reinterpret_cast<const char*>(&infoHeader), sizeof(infoHeader));
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<const 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 (size_t i = 0; i < rleData.size(); i++) {
totalRecords += (int)rleData[i].size();
for (size_t j = 0; j < rleData[i].size(); j++) {
totalPixels += rleData[i][j].count;
}
}
std::cout << "\n=== СТАТИСТИКА RLE СЖАТИЯ ===" << std::endl;
std::cout << "Всего пикселей: " << totalPixels << std::endl;
std::cout << "Всего RLE записей: " << totalRecords << std::endl;
if (totalRecords > 0) {
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;
int maxRows = std::min((int)rleData.size(), rows);
for (int i = 0; i < maxRows; i++) {
std::cout << "\nСтрока " << i << ": ";
int count = 0;
for (size_t j = 0; j < rleData[i].size(); j++) {
if (count++ > 10) {
std::cout << "...";
break;
}
std::cout << "["
<< (int)rleData[i][j].color.r << ","
<< (int)rleData[i][j].color.g << ","
<< (int)rleData[i][j].color.b
<< "x" << rleData[i][j].count << "] ";
}
}
}
// Главная функция
int main() {
SetConsoleOutputCP(1251);
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();
std::cout << "Применение RLE сжатия..." << std::endl;
std::vector<std::vector<RLERecord>> 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;
}