1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the WBMP plugin in the Qt ImageFormats module.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qwbmphandler_p.h"
41
42/*!
43 \class QWbmpHandler
44 \since 5.0
45 \brief The QWbmpHandler class provides support for the WBMP image format.
46 \internal
47*/
48
49#include <qimage.h>
50#include <qvariant.h>
51
52QT_BEGIN_NAMESPACE
53
54// This struct represents header of WBMP image file
55struct WBMPHeader
56{
57 quint8 type; // Type of WBMP image (always equal to 0)
58 quint8 format; // Format of WBMP image
59 quint32 width; // Width of the image already decoded from multibyte integer
60 quint32 height; // Height of the image already decoded from multibyte integer
61};
62#define WBMPFIXEDHEADER_SIZE 2
63
64// Data renderers and writers which takes care of data alignment endiness and stuff
65static bool readMultiByteInt(QIODevice *iodev, quint32 *num)
66{
67 quint32 res = 0;
68
69 quint8 c;
70 unsigned int count = 0;
71 do {
72 // Do not allow to read longer
73 // then we can store in num
74 if (++count > sizeof(*num))
75 return false;
76
77 if (!iodev->getChar(c: reinterpret_cast<char *>(&c)))
78 return false;
79
80 res = (res << 7) | (c & 0x7F);
81
82 } while (c & 0x80);
83
84 *num = res;
85 return true;
86}
87
88static bool writeMultiByteInt(QIODevice *iodev, quint32 num)
89{
90 quint64 tmp = num & 0x7F;
91 num >>= 7;
92
93 while (num) {
94 quint8 c = num & 0x7F;
95 num = num >> 7;
96 tmp = (tmp << 8) | (c | 0x80);
97 }
98
99 while (tmp) {
100 quint8 c = tmp & 0xFF;
101 if (!iodev->putChar(c))
102 return false;
103 tmp >>= 8;
104 }
105 return true;
106}
107
108static bool readWBMPHeader(QIODevice *iodev, WBMPHeader *hdr)
109{
110 if (!iodev)
111 return false;
112
113 uchar tmp[WBMPFIXEDHEADER_SIZE];
114 if (iodev->read(data: reinterpret_cast<char *>(tmp), WBMPFIXEDHEADER_SIZE) == WBMPFIXEDHEADER_SIZE) {
115 hdr->type = tmp[0];
116 hdr->format = tmp[1];
117 } else {
118 return false;
119 }
120
121 if (readMultiByteInt(iodev, num: &hdr->width)
122 && readMultiByteInt(iodev, num: &hdr->height)) {
123 return true;
124 }
125 return false;
126}
127
128static bool writeWBMPHeader(QIODevice *iodev, const WBMPHeader &hdr)
129{
130 if (iodev) {
131 uchar tmp[WBMPFIXEDHEADER_SIZE];
132 tmp[0] = hdr.type;
133 tmp[1] = hdr.format;
134 if (iodev->write(data: reinterpret_cast<char *>(tmp), WBMPFIXEDHEADER_SIZE) != WBMPFIXEDHEADER_SIZE)
135 return false;
136
137 if (writeMultiByteInt(iodev, num: hdr.width) &&
138 writeMultiByteInt(iodev, num: hdr.height))
139 return true;
140 }
141 return false;
142}
143
144static bool writeWBMPData(QIODevice *iodev, const QImage &image)
145{
146 if (iodev) {
147 int h = image.height();
148 int bpl = (image.width() + 7) / 8;
149
150 for (int l=0; l<h; l++) {
151 if (iodev->write(data: reinterpret_cast<const char *>(image.constScanLine(l)), len: bpl) != bpl)
152 return false;
153 }
154 return true;
155 }
156 return false;
157}
158
159static bool readWBMPData(QIODevice *iodev, QImage &image)
160{
161 if (iodev) {
162 int h = image.height();
163 int bpl = (image.width() + 7) / 8;
164
165 for (int l = 0; l < h; l++) {
166 if (iodev->read(data: reinterpret_cast<char *>(image.scanLine(l)), maxlen: bpl) != bpl)
167 return false;
168 }
169 return true;
170 }
171 return false;
172}
173
174class WBMPReader
175{
176public:
177 WBMPReader(QIODevice *iodevice);
178
179 QImage readImage();
180 bool writeImage(QImage image);
181
182 static bool canRead(QIODevice *iodevice);
183
184private:
185 QIODevice *iodev;
186 WBMPHeader hdr;
187};
188
189// WBMP common reader and writer implementation
190WBMPReader::WBMPReader(QIODevice *iodevice) : iodev(iodevice)
191{
192 memset(s: &hdr, c: 0, n: sizeof(hdr));
193}
194
195QImage WBMPReader::readImage()
196{
197 if (!readWBMPHeader(iodev, hdr: &hdr))
198 return QImage();
199
200 QImage image(hdr.width, hdr.height, QImage::Format_Mono);
201 if (!readWBMPData(iodev, image))
202 return QImage();
203
204 return image;
205}
206
207bool WBMPReader::writeImage(QImage image)
208{
209 if (image.format() != QImage::Format_Mono)
210 image = image.convertToFormat(f: QImage::Format_Mono);
211
212 if (image.colorTable().at(i: 0) == image.colorTable().at(i: 1)) {
213 // degenerate image: actually blank.
214 image.fill(pixel: (qGray(rgb: image.colorTable().at(i: 0)) < 128) ? 0 : 1);
215 } else if (qGray(rgb: image.colorTable().at(i: 0)) > qGray(rgb: image.colorTable().at(i: 1))) {
216 // Conform to WBMP's convention about black and white
217 image.invertPixels();
218 }
219
220 hdr.type = 0;
221 hdr.format = 0;
222 hdr.width = image.width();
223 hdr.height = image.height();
224
225 if (!writeWBMPHeader(iodev, hdr))
226 return false;
227
228 if (!writeWBMPData(iodev, image))
229 return false;
230
231 return true;
232}
233
234bool WBMPReader::canRead(QIODevice *device)
235{
236 if (device) {
237
238 if (device->isSequential())
239 return false;
240
241 // Save previous position
242 qint64 oldPos = device->pos();
243
244 WBMPHeader hdr;
245 if (readWBMPHeader(iodev: device, hdr: &hdr)) {
246 if ((hdr.type == 0) && (hdr.format == 0)) {
247 const qint64 imageSize = hdr.height * ((qint64(hdr.width) + 7) / 8);
248 qint64 available = device->bytesAvailable();
249 device->seek(pos: oldPos);
250 return (imageSize == available);
251 }
252 }
253 device->seek(pos: oldPos);
254 }
255 return false;
256}
257
258/*!
259 Constructs an instance of QWbmpHandler initialized to use \a device.
260*/
261QWbmpHandler::QWbmpHandler(QIODevice *device) :
262 m_reader(new WBMPReader(device))
263{
264}
265
266/*!
267 Destructor for QWbmpHandler.
268*/
269QWbmpHandler::~QWbmpHandler()
270{
271 delete m_reader;
272}
273
274/*!
275 * Verifies if some values (magic bytes) are set as expected in the header of the file.
276 * If the magic bytes were found, it is assumed that the QWbmpHandler can read the file.
277 */
278bool QWbmpHandler::canRead() const
279{
280 bool bCanRead = false;
281
282 QIODevice *device = QImageIOHandler::device();
283 if (device) {
284 bCanRead = QWbmpHandler::canRead(device);
285 if (bCanRead)
286 setFormat("wbmp");
287
288 } else {
289 qWarning(msg: "QWbmpHandler::canRead() called with no device");
290 }
291
292 return bCanRead;
293}
294
295/*! \reimp
296*/
297bool QWbmpHandler::read(QImage *image)
298{
299 bool bSuccess = false;
300 QImage img = m_reader->readImage();
301
302 if (!img.isNull()) {
303 bSuccess = true;
304 *image = img;
305 }
306
307 return bSuccess;
308}
309
310/*! \reimp
311*/
312bool QWbmpHandler::write(const QImage &image)
313{
314 if (image.isNull())
315 return false;
316
317 return m_reader->writeImage(image);
318}
319
320/*!
321 Only Size option is supported
322*/
323QVariant QWbmpHandler::option(ImageOption option) const
324{
325 if (option == QImageIOHandler::Size) {
326 QIODevice *device = QImageIOHandler::device();
327 if (device->isSequential())
328 return QVariant();
329
330 // Save old position
331 qint64 oldPos = device->pos();
332
333 WBMPHeader hdr;
334 if (readWBMPHeader(iodev: device, hdr: &hdr)) {
335 device->seek(pos: oldPos);
336 return QSize(hdr.width, hdr.height);
337 }
338
339 device->seek(pos: oldPos);
340
341 } else if (option == QImageIOHandler::ImageFormat) {
342 return QVariant(QImage::Format_Mono);
343 }
344
345 return QVariant();
346}
347
348bool QWbmpHandler::supportsOption(ImageOption option) const
349{
350 return (option == QImageIOHandler::Size) ||
351 (option == QImageIOHandler::ImageFormat);
352}
353
354bool QWbmpHandler::canRead(QIODevice *device)
355{
356 return WBMPReader::canRead(device);
357}
358
359QT_END_NAMESPACE
360

source code of qtimageformats/src/plugins/imageformats/wbmp/qwbmphandler.cpp