C++ 从XBM创建QImage

C++ 从XBM创建QImage,c++,qt,qimage,qt4.8,C++,Qt,Qimage,Qt4.8,从格式中存储的原始数据创建QBitmap,可以像这样轻松完成QBitmap::fromData(宽度、高度、数据、图像::格式), 数据为XBM文件的原始数据 现在,如果这次我们想创建一个QImage,该怎么办?事实上,假设我们想从相同的XBM原始数据创建一个QImage。 不过还有一个限制:此处不应使用QBitmap,这意味着将QBitmap转换为QImage将不是一个可接受的解决方案 我尝试了QImage(数据、宽度、高度、QImage::Format\u monosb) 但它不能生成正确的

从格式中存储的原始数据创建
QBitmap
,可以像这样轻松完成
QBitmap::fromData(宽度、高度、数据、图像::格式)
数据
为XBM文件的原始数据

现在,如果这次我们想创建一个
QImage
,该怎么办?
事实上,假设我们想从相同的XBM原始数据创建一个
QImage
。 不过还有一个限制:此处不应使用
QBitmap
,这意味着将
QBitmap
转换为
QImage
将不是一个可接受的解决方案

我尝试了
QImage(数据、宽度、高度、QImage::Format\u monosb)
但它不能生成正确的图像,因为
QImage
希望位与32位对齐,参见Qt的4.8文档:

QImage::QImage(uchar*数据、整数宽度、整数高度、格式)
构造具有给定宽度、高度和格式的图像,该图像使用 现有的内存缓冲区、数据。宽度和高度必须相同 以像素为单位指定,数据必须是32位对齐的,并且 图像中的数据也必须是32位对齐的

问题是XBM是1位对齐的(不是32位对齐的),因此由
QImage(数据、宽度、高度、QImage::Format_monosb)
生成的
QImage
是垃圾(未定义的行为,“随机”图像)

那么,如何从XBM的原始
数据创建
QImage


编辑: 以下是一个XBM文件,QImage应使用该文件构建:

#define x_width 66
#define x_height 27
static char x_bits[] = {
   0xff, 0xff, 0xff, 0xff, 0xff,
   0xff, 0xff, 0xff, 0xff, ...
};

这里是一个实现。它是线程安全的,如果有很多线程,您可以在多个线程上分发这些图像的构造。它一次处理32位输出

有必要将可用字节数从LittleEndian传递到
,以避免读取超过输入数据的末尾

#include <QtWidgets>
#include <cstdint>

inline bool isAlignedTo(const void * p, int bits) {
  return (reinterpret_cast<uintptr_t>(p) & ((bits/8)-1)) == 0;
}

inline uint32_t fromLittleEndian(const void *p, int bytes) {
  Q_ASSERT(bytes > 0);
  uint32_t val;
  if (bytes >= 4) {
    val = *reinterpret_cast<const uint32_t*>(p);
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
    val = val >> 24 | (val >> 8 & 0xFF00U) | (val << 8 & 0xFF0000U) | val << 24;
#endif
  } else {
    auto pp = reinterpret_cast<const uint8_t*>(p);
    val = *pp++;
    if (bytes > 1) val |= static_cast<uint32_t>(*pp++) << 8;
    if (bytes > 2) val |= static_cast<uint32_t>(*pp) << 16;
  }
  return val;
}

QImage fromXBM(const uchar * data, int width, int height,
               QImageCleanupFunction cleanup = 0, void * cleanupData = 0) {
  Q_ASSERT(width && height);
  if ((width & 31) == 0 && isAlignedTo(data, 32)) // 32-bit aligned
    return QImage(data, width, height, QImage::Format_MonoLSB, cleanup, cleanupData);
  if ((width & 7) == 0) // 8-bit aligned
    return QImage(data, width, height, width/8, QImage::Format_MonoLSB, cleanup, cleanupData);

  // Note: Source pixels are stored LSB first, MSB last.
  QImage img(width, height, QImage::Format_MonoLSB);
  Q_ASSERT((img.bytesPerLine() & 0x3) == 0); // must be 32-bit aligned
  uint32_t * dst = reinterpret_cast<uint32_t*>(img.bits());
  const int wordsPerRow = width / 32 + ((width & 31) ? 1 : 0);
  int shift = 0;
  int bytesLeft = (width * height)/8 + ((width * height & 7) ? 1 : 0);
  uint32_t src = fromLittleEndian(data, bytesLeft);
  bytesLeft -= 4;
  data += 4;
  while (height--) {
    QList<uint32_t> w;
    int widthLeft = width;
    for (int c = 0; c < wordsPerRow; ++c) {
      Q_ASSERT(widthLeft > 0);
      uint32_t word = src >> shift;
      if (32-shift < widthLeft) {
        src = fromLittleEndian(data, bytesLeft);
        bytesLeft -= 4;
        data += 4;
        if (shift) word |= src << (32-shift);
      }
      widthLeft -= 32;
      *dst++ = word;
    }
    shift = (shift + width) & 31;
  }
  // Dispose of the data since we've made a copy
  if (cleanup) cleanup(cleanupData);
  return img;
}

// w = 5 h = 5
// 1...1   0x11,3
// .1.1.   0x40 0x01,6
// ..1..        0x10,1
// .1.1.        0x00 0x05,4
// 1...1             0x10  0x01,7
//         0x51 0x11 0x15  0x01

const int X_w = 5, X_h = 5;
const uchar X_bits[] = { 0x51, 0x11, 0x15, 0x01 };

// 17 iterations of rule 30 cellular automaton, see
// Stephen Wolfram, The New Kind Of Science
// from https://lost-contact.mit.edu/afs/hep.wisc.edu/apps/Mathematica-7.0/Documentation/English/System/ExampleData/
const int rule30_w = 40, rule30_h = 17;
const uchar rule30_bits[] = {
 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0xc0,
 0x04, 0x00, 0x00, 0x00, 0x60, 0x0f, 0x00, 0x00, 0x00, 0x30, 0x11, 0x00,
 0x00, 0x00, 0xd8, 0x3b, 0x00, 0x00, 0x00, 0x4c, 0x48, 0x00, 0x00, 0x00,
 0xf6, 0xfc, 0x00, 0x00, 0x00, 0x13, 0x07, 0x01, 0x00, 0x80, 0xbd, 0x89,
 0x03, 0x00, 0xc0, 0x84, 0xde, 0x04, 0x00, 0x60, 0xcf, 0x42, 0x0f, 0x00,
 0x30, 0x71, 0x66, 0x11, 0x00, 0xd8, 0x9b, 0x3b, 0x3b, 0x00, 0x4c, 0xe8,
 0xc8, 0x49, 0x00, 0xf6, 0x2c, 0x7d, 0xfe, 0x00, 0x13, 0xe7, 0x85, 0x03,
 0x01
};

int main(int argc, char *argv[])
{
  QApplication a(argc, argv);
  auto X = fromXBM(X_bits, X_w, X_h);
  X.invertPixels();
  auto bigX = X.scaled(X.size() * 4);
  auto rule30 = fromXBM(rule30_bits, rule30_w, rule30_h);
  rule30.invertPixels();
  auto big30 = rule30.scaled(rule30.size()*2);
  QWidget w;
  QHBoxLayout layout(&w);
  QLabel labelX, label30;
  layout.addWidget(&labelX);
  layout.addWidget(&label30);
  labelX.setPixmap(QPixmap::fromImage(bigX));
  label30.setPixmap(QPixmap::fromImage(big30));
  w.show();
  return a.exec();
}

#包括
#包括
内联布尔值对齐到(常量无效*p,整数位){
返回(重新解释(p)和((比特/8)-1))==0;
}
来自LittleEndian的内联uint32_t(常量void*p,int字节){
Q_断言(字节>0);
uint32_t val;
如果(字节>=4){
val=*重新解释铸型(p);
#如果Q_BYTE_ORDER==Q_BIG_ENDIAN
val=val>>24 |(val>>8&0xFF00U)|(val>shift;
如果(32档<左档){
src=fromlitleendian(数据,字节左);
字节左-=4;
数据+=4;

如果(shift)单词|=src正常,那么Qt-alreay已经实现了一个简单的代码,该代码从
XBM
的原始数据构建
QImage

QImage image(size, monoFormat);
image.setColor(0, QColor(Qt::color0).rgb());
image.setColor(1, QColor(Qt::color1).rgb());

// Need to memcpy each line separatly since QImage is 32bit aligned and
// this data is only byte aligned...
int bytesPerLine = (size.width() + 7) / 8;
for (int y = 0; y < size.height(); ++y)
{
   memcpy(image.scanLine(y), bits + bytesPerLine * y, bytesPerLine);
}
QImage图像(大小,单格式);
setColor(0,QColor(Qt::color0.rgb());
setColor(1,QColor(Qt::color1.rgb());
//由于QImage是32位对齐且
//此数据仅与字节对齐。。。
int bytesPerLine=(size.width()+7)/8;
对于(int y=0;y
此代码用于
qbitmap.cpp
,更具体地说,用于:

QBitmap::fromData(const-QSize&size,const-uchar*bits,QImage::Format-monoFormat)

QImage-image;image.loadFromData(data,dataLen,“xbm”);
?第三个参数是字符串
“xbm”
,如果您想让Qt自动检测,则什么都没有(鉴于xbm没有头,我不确定会发生,IIRC)。换句话说,这是图像数据的格式。@KubaOber是的,的确,我一定是弄错了QImage构造函数中的参数。无论如何,即使是在传递“XBM”时作为第三个参数,它不起作用,因为
loadFromData
希望接收XBM文件的实际内容,而我只能在
静态字符x_位
数组中提供数据(参见编辑)不,性能不是这里不能使用
QBitmap
的原因。事实上,这个约束与事实
QBitmap
有关,不能在GUI线程之外使用。但是
QImage
可以,这就是为什么我们首先要从
QBitmap
迁移到
QImage
)所以,正如我之前所说,
QBitmap
不是一个可接受的解决方案:)@gpalex如果你坚持不使用Qt,你可以自己使用。请参阅编辑。