使用 QFile 使 istringstream 作为二进制输入(对于 libpng)

Using QFile made istringstream as binary input (for libpng)

我正在尝试使用 libpng 从中读取 png Qt 资源。问题:class 正在阅读 应该有Qt的任何依赖性。

第一步,阅读http://www.piko3d.net/tutorials/libpng-tutorial-loading-png-files-from-streams/#CustomRead我已经成功写了一个函数

read_png(istream& in)

我也成功传递了一个普通的旧 ifstream

ifstream in("abs_path_to_png/icon.png");

read_png(..)并成功读取png。但是如何获得 来自 Qt 资源的(最好是独立于平台的)istream?表现 不是什么大问题所以我最初想出了

bool Io_Qt::get_istringstream_from_QFile(QFile& qfile, istringstream& iss)
{
    // [.. Some checking for existence and the file being open ..]
    QString qs(qfile.readAll());
    iss.str(qs.toStdString());

    // I also tried: QByteArray arr(qfile.readAll()); iss.str(arr.data());

    return qfile.isOpen();
}

// Someplace else iss and qfile are created like this:

istringstream iss(std::stringstream::in | std::stringstream::binary);
QFile qfile(":/res/icon.png");
qfile.open(QIODevice::ReadOnly);

这实际上产生了一个问题,即乍一看,看起来不错,当说

cout << "'" << iss.str().c_str() << "'" << endl;

我明白了

'�PNG

'

虽然似乎存在一些空白问题。对于

ifstream in("abs_path_to_png/icon.png");
char c;
cout << "'";
for (int j=0;j<8;j++)
{
    in >> c;
    cout << c;
}
cout << "'" << endl;

产量

'�PNG'

虽然后者有效,但前一个变体最终导致 libpng 检查函数 png_sig_cmp(..) 拒绝我的 png 为无效。我的第一反应是 "binary"。然而:

  1. istringstream iss(std::stringstream::in | std::stringstream::binary);感觉不错。
  2. QIODevice::ReadOnly 似乎没有二进制伙伴。

你看到我错过了什么了吗?

您正在处理流,就像处理带有词法提取运算符的文本数据一样。查看 ios::binary 以及适用于二进制流的 readwrite 方法。

我会完全放弃 operator<<operator>> 以支持 readwrite。使用 ostream::write 写入从 QIODevice::readAll() 返回的字节数组数据以将其内容传输到您的临时字符串流,例如,并在您的测试中使用 ostream::read 来验证其内容。

确保正确传输的一个很好的测试用例是编写一个测试,从 QFile 读取内容,使用 ostream::write 将其传输到二进制输出文件流(ofstream ),然后尝试用图像软件加载它,看看是否可以。然后将您的文件流与字符串流交换,并在您工作时将其传递给 libpng

正如艾克所说,这似乎确实是关于两者之间的差异 以文本为中心的运算符“>>”、“<<”和类似“.str(..)”的东西 以二进制为中心的命令,如“.read”和“.write”。加上它是 关于正确初始化流。当我终于得到程序 做我想做的福音是这样的:

首先,我在 QFile 旁边使用了一个普通的字符串流:

// Explicitly setting flags should at least contain ::in and ::out
// stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary)
// However, the default constructor is perfectly fine.
stringstream ss;
QFile qfile(":/res/icon.png");
qfile.open(QIODevice::ReadOnly);

这是我传递给我的函数,现在看起来像这样:

bool Io_Qt::get_stringstream_from_QFile(QFile& qfile, stringstream& ss)
{
    // [.. some sanity checks..]
    QDataStream in(&qfile);
    uint len = qfile.size();
    char* c = (char*)malloc(len*sizeof(char));
    in.readRawData(c,len);
    ss.write(c,len);
    free (c);
    return true;
}

此流已填充,并且大小合适。特别是从 .write(..) 写入所需数量的字符,无论 数据中有多少个零。我最大的问题是 我不愿意同时拥有 std::stringstream::in 和 std::stringstream::out同时激活因为 组合对我来说似乎有些古怪。然而两者都是需要的。 但是,我发现我可以跳过std::stringstream::binary。 但是因为它似乎没有任何伤害我喜欢 保持好运。不过,请随意评论这个迷信! :-)

更干净、更少 C 风格、更多 Qt/C++ 风格的版本可以是:

QFile file(filePath);
file.open(QIODevice::ReadOnly);
QByteArray data = file.readAll();
std::istringstream iss(data.toStdString());

现在使用 iss,在我的例子中,这是用于 libTIFF:

TIFF* tif = TIFFStreamOpen("MemTIFF", &iss);
// ...

此外,对于 PNG,您现在可以遵循 already posted article,因为 std::istringstream 的类型为 std::istream

请注意,此解决方案涉及将文件数据完全加载到内存中。