如何使用cairomm的PNG流进行读写操作?
问题描述
我需要将Cairo::ImageSurface保存为二进制文件的一部分,用surface->get_data()写入像素数据可行,但生成的文件远大于surface->write_to_png(std::string name)的结果,因此尝试使用write_to_png_stream和create_from_png_stream接口。
我能理解SlotWriteFunc的逻辑:写入时会被多次调用,每次传入PNG数据块的指针和长度。但SlotReadFunc的参数是unsigned int类型的期望读取长度,而非引用类型,无法返回实际读取的字节数,这让我困惑,也说明我对两个回调的理解存在偏差。
目前我的代码写入文件正常,但调用create_from_png_stream时抛出错误:
terminate called after throwing an instance of 'std::bad_alloc' what(): std::bad_alloc Aborted
示例代码
#include <fstream> #include <iostream> #include <cairomm/cairomm.h> class PNGStream { public: std::fstream *file; Cairo::ErrorStatus write(const unsigned char *data, unsigned int length) { std::cout << length << std::endl; file->write(reinterpret_cast<char *>(&length), sizeof(length)); file->write(reinterpret_cast<const char*>(data), length); return Cairo::ErrorStatus::CAIRO_STATUS_SUCCESS; } Cairo::ErrorStatus read(unsigned char *data, unsigned int length) { std::cout << length << std::endl; // I'm not sure, what comes here return Cairo::ErrorStatus::CAIRO_STATUS_SUCCESS; } }; int main() { std::fstream file; PNGStream png_stream; png_stream.file = &file; auto surface = Cairo::ImageSurface::create(Cairo::Surface::Format::ARGB32, 1920, 1080); auto cr = Cairo::Context::create(surface); cr->set_source_rgb(0, 0, 0); cr->paint(); file.open("test.png", std::ios::binary | std::ios::out); surface->write_to_png_stream(sigc::mem_fun(png_stream, &PNGStream::write)); file.close(); file.open("test.png", std::ios::binary | std::ios::in); auto surface2 = Cairo::ImageSurface::create_from_png_stream(sigc::mem_fun(png_stream, &PNGStream::read)); //error file.close(); return 0; }
回调接口正确用法说明
1. SlotWriteFunc 正确逻辑
Cairo会将标准PNG格式的压缩数据分块传给回调,你只需要直接将传入的data(长度为length)写入目标载体即可,不需要额外写入length本身。你的代码中错误地每次先写入块长度,导致保存的文件不是标准PNG格式,这是后续读取失败的核心原因。
2. SlotReadFunc 正确逻辑
Cairo传入的length是它当前需要读取的字节数,你需要从数据源读取最多length字节的数据填充到data缓冲区:
- 如果实际读取的字节数少于
length(比如到文件末尾),直接返回CAIRO_STATUS_SUCCESS即可,Cairo会自动处理EOF情况 - 不需要返回实际读取长度,Cairo会根据读取的PNG内容判断解析进度
- 若读取过程中出现错误,返回对应的错误状态(如
CAIRO_STATUS_READ_ERROR)
修正后的代码
#include <fstream> #include <iostream> #include <cairomm/cairomm.h> class PNGStream { public: std::fstream* file; Cairo::ErrorStatus write(const unsigned char* data, unsigned int length) { std::cout << "Writing " << length << " bytes\n"; // 直接写入Cairo提供的PNG数据块,无需额外写入长度 file->write(reinterpret_cast<const char*>(data), length); // 检查写入错误 if (file->fail()) { return Cairo::ErrorStatus::CAIRO_STATUS_WRITE_ERROR; } return Cairo::ErrorStatus::CAIRO_STATUS_SUCCESS; } Cairo::ErrorStatus read(unsigned char* data, unsigned int length) { std::cout << "Reading up to " << length << " bytes\n"; // 读取指定长度的数据到缓冲区 file->read(reinterpret_cast<char*>(data), length); // 检查严重读取错误 if (file->bad()) { return Cairo::ErrorStatus::CAIRO_STATUS_READ_ERROR; } // 即使实际读取字节数不足,也返回成功,Cairo会处理EOF return Cairo::ErrorStatus::CAIRO_STATUS_SUCCESS; } }; int main() { std::fstream file; PNGStream png_stream; png_stream.file = &file; // 创建并绘制Surface auto surface = Cairo::ImageSurface::create(Cairo::Surface::Format::ARGB32, 1920, 1080); auto cr = Cairo::Context::create(surface); cr->set_source_rgb(0, 0, 0); cr->paint(); // 写入PNG流 file.open("test.png", std::ios::binary | std::ios::out); if (!file.is_open()) { std::cerr << "Failed to open file for writing\n"; return 1; } surface->write_to_png_stream(sigc::mem_fun(png_stream, &PNGStream::write)); file.close(); // 读取PNG流 file.open("test.png", std::ios::binary | std::ios::in); if (!file.is_open()) { std::cerr << "Failed to open file for reading\n"; return 1; } try { auto surface2 = Cairo::ImageSurface::create_from_png_stream(sigc::mem_fun(png_stream, &PNGStream::read)); std::cout << "Successfully loaded surface: " << surface2->get_width() << "x" << surface2->get_height() << "\n"; } catch (const std::exception& e) { std::cerr << "Error loading surface: " << e.what() << "\n"; return 1; } file.close(); return 0; }
内容的提问来源于stack exchange,提问作者mltm
相关产品推荐
相关产品推荐

