#include #include #include #include #include "msolimageiohandler.h" static const int header_size = 16; namespace { static inline uchar encode_color(QRgb c) { return (qRed(c) & 0xC0) | ((qBlue(c) & 0xC0) >> 2) | ((qGreen(c) & 0xC0) >> 4) | 3; } static inline QRgb decode_color(uchar p, uchar a = 255) { return qRgba(p & 0xC0, (p & 0x30) << 2, (p & 0x0C) << 4, a); } } MSOLImageIOHandler::MSOLImageIOHandler() : QImageIOHandler() { } bool MSOLImageIOHandler::canRead() const { char header[header_size]; if (device()->peek(header, header_size) != header_size) { return false; } return strncmp(&header[0], "MSOL ", 6) == 0; } bool MSOLImageIOHandler::read(QImage *image) { QIODevice *dev = device(); uchar header[header_size]; if (dev->read(reinterpret_cast(header), header_size) != header_size) { return false; } if (strncmp(reinterpret_cast(&header[0]), "MSOL ", 6) != 0) { return false; } const bool alpha = header[6]; const int width = qFromLittleEndian(&header[8]); const int height = qFromLittleEndian(&header[10]); const int pixel_size = alpha ? 2 : 1; const int line_size = width * pixel_size; if (alpha) { *image = QImage(width, height, QImage::Format_ARGB32); } else { *image = QImage(width, height, QImage::Format_RGB32); } Q_ASSERT(image->depth() == sizeof(QRgb) * 8); for (int y = 0; y < height; y++) { QRgb *dst = reinterpret_cast(image->scanLine(y)); uchar src[line_size]; if (dev->read(reinterpret_cast(src), line_size) != line_size) { qDebug() << "Could not read image line" << y; return false; } for (int x = 0; x < width; x++) { if (alpha) { dst[x] = decode_color(src[x * pixel_size]); } else { dst[x] = decode_color(src[x * pixel_size], src[(x * pixel_size) + 1]); } } } return true; } bool MSOLImageIOHandler::write(const QImage &image) { QIODevice *dev = device(); const int height = image.height(); const int width = image.width(); const bool alpha = image.hasAlphaChannel(); const int pixel_size = alpha ? 2 : 1; const int line_size = width * pixel_size; QImage img = image.convertToFormat(alpha ? QImage::Format_ARGB32 : QImage::Format_RGB32); uchar header[header_size] = { 0 }; strcpy(reinterpret_cast(&header[0]), "MSOL "); if (alpha) header[6] = 0x80; header[7] = 8; qToLittleEndian(width, &header[8]); qToLittleEndian(height, &header[10]); if (dev->write(reinterpret_cast(header), header_size) != header_size) { qDebug() << "Could not write header"; return false; } Q_ASSERT(img.depth() == sizeof(QRgb) * 8); for (int y = 0; y < height; y++) { uchar dst[line_size]; const QRgb *src = reinterpret_cast(img.constScanLine(y)); for (int x = 0; x < width; x++) { dst[x * pixel_size] = encode_color(src[x]); if (alpha) { dst[(x * pixel_size) + 1] = qAlpha(src[x]); } } if (dev->write(reinterpret_cast(dst), line_size) != line_size) { qDebug() << "Could not write image line " << y; return false; } } return true; } bool MSOLImageIOHandler::supportsOption(ImageOption option) const { switch (option) { case Size: return true; default: return false; } } QVariant MSOLImageIOHandler::option(ImageOption option) const { char header[header_size]; switch (option) { case Size: if (device()->peek(header, header_size) == header_size) { const QSize size(qFromLittleEndian(reinterpret_cast(&header[8])), qFromLittleEndian(reinterpret_cast(&header[10]))); return QVariant::fromValue(size); } else { qDebug() << "Cannot read from device: " << device()->errorString(); } default: break; } return QVariant(); } void MSOLImageIOHandler::setOption(ImageOption option, const QVariant &value) { }