You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 19:46:18