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

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