Here is one implementation. It is thread-safe and you could distribute the construction of such images over multiple threads, if there's very many of them. It processes 32 bits of output at the time.
It is necessary to pass the number of available bytes to fromLittleEndian
to avoid reading past the end of the input data.
#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();
}
QImage image; image.loadFromData(data, dataLen, "xbm");
? – peppe"xbm"
, or nothing if you want Qt to autodetect (which I'm not sure it would happen given that XBM has no headers, IIRC). In other words it's the format of the image data. – peppeloadFromData
expects to recieve the actual content of the XBM file, whereas I can only provide the data inside thestatic char x_bits
array (cf. EDIT). – gpalex