#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <chrono>
#include <thread>
#include <cstdio>
#include <windows.h>
//作者CatGPT,luogu_tanbozhi, LuoguOItanbozhi
//压缩调0.25,0.15
//帧率60
// 扩展后的灰度字符映射:70阶灰度
const char* grayScaleChars = "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~<>i!lI;:,\"^`'. ";
// 读取PPM图像文件并返回图像数据,增加了读写加速
std::vector<unsigned char> readPPM(const std::string& filename, int& width, int& height) {
std::ifstream file(filename, std::ios::binary);
// 通过设置读取缓冲区来加速读取
char buffer[4096];
file.rdbuf()->pubsetbuf(buffer, sizeof(buffer));
if (!file.is_open()) {
throw std::runtime_error("无法打开文件: " + filename);
}
std::string format;
file >> format; // 读取文件头 "P6"
if (format != "P6") {
throw std::runtime_error("不支持的PPM格式: " + format);
}
file >> width >> height;
int maxColorValue;
file >> maxColorValue; // 通常为255
file.ignore(1); // 跳过一个换行符
// 读取图像数据 (RGB格式)
std::vector<unsigned char> imageData(width * height * 3); // 每个像素有3个通道 (RGB)
file.read(reinterpret_cast<char*>(imageData.data()), imageData.size());
return imageData;
}
// 将RGB像素转换为灰度值
unsigned char rgbToGray(unsigned char r, unsigned char g, unsigned char b) {
return static_cast<unsigned char>(0.3 * r + 0.59 * g + 0.11 * b); // 常见的灰度转换公式
}
// 将灰度值映射为ASCII字符
char grayToAscii(unsigned char grayValue) {
// 使用70阶灰度映射
int index = (grayValue * 69) / 255; // 通过除以255来映射到[0, 69]的字符索引
return grayScaleChars[index];
}
// 显示ASCII图像,保持横纵比例
void displayAsciiImage(const std::vector<unsigned char>& imageData, int width, int height, double horizontalCompression, double verticalCompression) {
std::string asciiImage;
int newWidth = static_cast<int>(width * horizontalCompression); // 水平方向压缩
int newHeight = static_cast<int>(height * verticalCompression); // 垂直方向压缩
for (int y = 0; y < newHeight; ++y) {
for (int x = 0; x < newWidth; ++x) {
int idx = (static_cast<int>(y / verticalCompression) * width + static_cast<int>(x / horizontalCompression)) * 3; // 根据压缩率映射像素
unsigned char r = imageData[idx];
unsigned char g = imageData[idx + 1];
unsigned char b = imageData[idx + 2];
unsigned char gray = rgbToGray(r, g, b); // 转换为灰度
asciiImage += grayToAscii(gray); // 转换为ASCII字符
}
asciiImage += '\n'; // 换行
}
std::system("cls"); // 清屏并将光标移至左上角
std::cout << asciiImage; // 输出ASCII图像
}
// 生成帧文件名
std::string generateFrameFilename(const std::string& pattern, int frameNumber) {
char buffer[100];
sprintf(buffer, pattern.c_str(), frameNumber);
return std::string(buffer);
}
int main() {
std::string pattern;
std::cout << "请输入.ppm文件名模式 (如 a_%03d.ppm): ";
std::cin >> pattern;
// 设置帧率
int fps = 30;
std::cout << "请输入帧率 (例如 30): ";
std::cin >> fps;
int frameDelay = 1000 / fps; // 每帧的时间间隔,单位:毫秒
// 设置最大帧数
int maxFrames = 10000; // 假设最大帧数为10000,实际情况根据你的文件数量调整
// 添加可调的横向和纵向压缩率
double horizontalCompression, verticalCompression;
std::cout << "请输入图像宽度压缩率 (例如 0.5 表示宽度缩放为原来的50%): ";
std::cin >> horizontalCompression;
std::cout << "请输入图像高度压缩率 (建议为1.0, 若调整比例不同可改变): ";
std::cin >> verticalCompression;
// 模拟多帧播放
for (int i = 0; i < maxFrames; ++i) {
std::string filename = generateFrameFilename(pattern, i + 1);
// 尝试读取帧文件
int width, height;
std::vector<unsigned char> imageData;
try {
imageData = readPPM(filename, width, height);
} catch (const std::exception& e) {
std::cerr << "无法读取帧: " << filename << " 错误: " << e.what() << std::endl;
break; // 如果文件读取失败,就停止播放
}
auto frameStart = std::chrono::high_resolution_clock::now();
// 显示当前帧的ASCII图像
displayAsciiImage(imageData, width, height, horizontalCompression, verticalCompression);
// 控制帧率
auto frameEnd = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> elapsed = frameEnd - frameStart;
if (elapsed.count() < frameDelay) {
std::this_thread::sleep_for(std::chrono::milliseconds(frameDelay) - elapsed);
}
}
return 0;
}