/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the WBMP plugin in the Qt ImageFormats module. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qwbmphandler_p.h" /*! \class QWbmpHandler \since 5.0 \brief The QWbmpHandler class provides support for the WBMP image format. \internal */ #include #include QT_BEGIN_NAMESPACE // This struct represents header of WBMP image file struct WBMPHeader { quint8 type; // Type of WBMP image (always equal to 0) quint8 format; // Format of WBMP image quint32 width; // Width of the image already decoded from multibyte integer quint32 height; // Height of the image already decoded from multibyte integer }; #define WBMPFIXEDHEADER_SIZE 2 // Data renderers and writers which takes care of data alignment endiness and stuff static bool readMultiByteInt(QIODevice *iodev, quint32 *num) { quint32 res = 0; quint8 c; unsigned int count = 0; do { // Do not allow to read longer // then we can store in num if (++count > sizeof(*num)) return false; if (!iodev->getChar(reinterpret_cast(&c))) return false; res = (res << 7) | (c & 0x7F); } while (c & 0x80); *num = res; return true; } static bool writeMultiByteInt(QIODevice *iodev, quint32 num) { quint64 tmp = num & 0x7F; num >>= 7; while (num) { quint8 c = num & 0x7F; num = num >> 7; tmp = (tmp << 8) | (c | 0x80); } while (tmp) { quint8 c = tmp & 0xFF; if (!iodev->putChar(c)) return false; tmp >>= 8; } return true; } static bool readWBMPHeader(QIODevice *iodev, WBMPHeader *hdr) { if (!iodev) return false; uchar tmp[WBMPFIXEDHEADER_SIZE]; if (iodev->read(reinterpret_cast(tmp), WBMPFIXEDHEADER_SIZE) == WBMPFIXEDHEADER_SIZE) { hdr->type = tmp[0]; hdr->format = tmp[1]; } else { return false; } if (readMultiByteInt(iodev, &hdr->width) && readMultiByteInt(iodev, &hdr->height)) { return true; } return false; } static bool writeWBMPHeader(QIODevice *iodev, const WBMPHeader &hdr) { if (iodev) { uchar tmp[WBMPFIXEDHEADER_SIZE]; tmp[0] = hdr.type; tmp[1] = hdr.format; if (iodev->write(reinterpret_cast(tmp), WBMPFIXEDHEADER_SIZE) != WBMPFIXEDHEADER_SIZE) return false; if (writeMultiByteInt(iodev, hdr.width) && writeMultiByteInt(iodev, hdr.height)) return true; } return false; } static bool writeWBMPData(QIODevice *iodev, const QImage &image) { if (iodev) { int h = image.height(); int bpl = (image.width() + 7) / 8; for (int l=0; lwrite(reinterpret_cast(image.constScanLine(l)), bpl) != bpl) return false; } return true; } return false; } static bool readWBMPData(QIODevice *iodev, QImage &image) { if (iodev) { int h = image.height(); int bpl = (image.width() + 7) / 8; for (int l = 0; l < h; l++) { if (iodev->read(reinterpret_cast(image.scanLine(l)), bpl) != bpl) return false; } return true; } return false; } class WBMPReader { public: WBMPReader(QIODevice *iodevice); QImage readImage(); bool writeImage(QImage image); static bool canRead(QIODevice *iodevice); private: QIODevice *iodev; WBMPHeader hdr; }; // WBMP common reader and writer implementation WBMPReader::WBMPReader(QIODevice *iodevice) : iodev(iodevice) { memset(&hdr, 0, sizeof(hdr)); } QImage WBMPReader::readImage() { if (!readWBMPHeader(iodev, &hdr)) return QImage(); QImage image(hdr.width, hdr.height, QImage::Format_Mono); if (!readWBMPData(iodev, image)) return QImage(); return image; } bool WBMPReader::writeImage(QImage image) { if (image.format() != QImage::Format_Mono) image = image.convertToFormat(QImage::Format_Mono); if (image.colorTable().at(0) == image.colorTable().at(1)) { // degenerate image: actually blank. image.fill((qGray(image.colorTable().at(0)) < 128) ? 0 : 1); } else if (qGray(image.colorTable().at(0)) > qGray(image.colorTable().at(1))) { // Conform to WBMP's convention about black and white image.invertPixels(); } hdr.type = 0; hdr.format = 0; hdr.width = image.width(); hdr.height = image.height(); if (!writeWBMPHeader(iodev, hdr)) return false; if (!writeWBMPData(iodev, image)) return false; return true; } bool WBMPReader::canRead(QIODevice *device) { if (device) { if (device->isSequential()) return false; // Save previous position qint64 oldPos = device->pos(); WBMPHeader hdr; if (readWBMPHeader(device, &hdr)) { if ((hdr.type == 0) && (hdr.format == 0)) { qint64 imageSize = hdr.height * ((hdr.width + 7) / 8); qint64 available = device->bytesAvailable(); device->seek(oldPos); return (imageSize == available); } } device->seek(oldPos); } return false; } /*! Constructs an instance of QWbmpHandler initialized to use \a device. */ QWbmpHandler::QWbmpHandler(QIODevice *device) : m_reader(new WBMPReader(device)) { } /*! Destructor for QWbmpHandler. */ QWbmpHandler::~QWbmpHandler() { delete m_reader; } /*! * Verifies if some values (magic bytes) are set as expected in the header of the file. * If the magic bytes were found, it is assumed that the QWbmpHandler can read the file. */ bool QWbmpHandler::canRead() const { bool bCanRead = false; QIODevice *device = QImageIOHandler::device(); if (device) { bCanRead = QWbmpHandler::canRead(device); if (bCanRead) setFormat("wbmp"); } else { qWarning("QWbmpHandler::canRead() called with no device"); } return bCanRead; } /*! \reimp */ bool QWbmpHandler::read(QImage *image) { bool bSuccess = false; QImage img = m_reader->readImage(); if (!img.isNull()) { bSuccess = true; *image = img; } return bSuccess; } /*! \reimp */ bool QWbmpHandler::write(const QImage &image) { if (image.isNull()) return false; return m_reader->writeImage(image); } /*! Only Size option is supported */ QVariant QWbmpHandler::option(ImageOption option) const { if (option == QImageIOHandler::Size) { QIODevice *device = QImageIOHandler::device(); if (device->isSequential()) return QVariant(); // Save old position qint64 oldPos = device->pos(); WBMPHeader hdr; if (readWBMPHeader(device, &hdr)) { device->seek(oldPos); return QSize(hdr.width, hdr.height); } device->seek(oldPos); } else if (option == QImageIOHandler::ImageFormat) { return QVariant(QImage::Format_Mono); } return QVariant(); } bool QWbmpHandler::supportsOption(ImageOption option) const { return (option == QImageIOHandler::Size) || (option == QImageIOHandler::ImageFormat); } bool QWbmpHandler::canRead(QIODevice *device) { return WBMPReader::canRead(device); } QT_END_NAMESPACE