source: trunk/src/gui/image/qxbmhandler.cpp@ 1056

Last change on this file since 1056 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 10.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <qplatformdefs.h>
43#include "private/qxbmhandler_p.h"
44
45#ifndef QT_NO_IMAGEFORMAT_XBM
46
47#include <qimage.h>
48#include <qiodevice.h>
49#include <qvariant.h>
50
51#include <stdio.h>
52#include <ctype.h>
53
54QT_BEGIN_NAMESPACE
55
56/*****************************************************************************
57 X bitmap image read/write functions
58 *****************************************************************************/
59
60static inline int hex2byte(register char *p)
61{
62 return ((isdigit((uchar) *p) ? *p - '0' : toupper((uchar) *p) - 'A' + 10) << 4) |
63 (isdigit((uchar) *(p+1)) ? *(p+1) - '0' : toupper((uchar) *(p+1)) - 'A' + 10);
64}
65
66static bool read_xbm_header(QIODevice *device, int& w, int& h)
67{
68 const int buflen = 300;
69 const int maxlen = 4096;
70 char buf[buflen + 1];
71 QRegExp r1(QLatin1String("^#define[ \t]+[a-zA-Z0-9._]+[ \t]+"));
72 QRegExp r2(QLatin1String("[0-9]+"));
73
74 qint64 readBytes = 0;
75 qint64 totalReadBytes = 0;
76
77 buf[0] = '\0';
78
79 // skip initial comment, if any
80 while (buf[0] != '#') {
81 readBytes = device->readLine(buf, buflen);
82
83 // if readBytes >= buflen, it's very probably not a C file
84 if (readBytes <= 0 || readBytes >= buflen -1)
85 return false;
86
87 // limit xbm headers to the first 4k in the file to prevent
88 // excessive reads on non-xbm files
89 totalReadBytes += readBytes;
90 if (totalReadBytes >= maxlen)
91 return false;
92 }
93
94 buf[readBytes - 1] = '\0';
95 QString sbuf;
96 sbuf = QString::fromLatin1(buf);
97
98 // "#define .._width <num>"
99 if (r1.indexIn(sbuf) == 0 &&
100 r2.indexIn(sbuf, r1.matchedLength()) == r1.matchedLength())
101 w = QByteArray(&buf[r1.matchedLength()]).trimmed().toInt();
102
103 // "#define .._height <num>"
104 readBytes = device->readLine(buf, buflen);
105 if (readBytes <= 0)
106 return false;
107 buf[readBytes - 1] = '\0';
108
109 sbuf = QString::fromLatin1(buf);
110
111 if (r1.indexIn(sbuf) == 0 &&
112 r2.indexIn(sbuf, r1.matchedLength()) == r1.matchedLength())
113 h = QByteArray(&buf[r1.matchedLength()]).trimmed().toInt();
114
115 // format error
116 if (w <= 0 || w > 32767 || h <= 0 || h > 32767)
117 return false;
118
119 return true;
120}
121
122static bool read_xbm_body(QIODevice *device, int w, int h, QImage *outImage)
123{
124 const int buflen = 300;
125 char buf[buflen + 1];
126
127 qint64 readBytes = 0;
128
129 // scan for database
130 for (;;) {
131 if ((readBytes = device->readLine(buf, buflen)) <= 0) {
132 // end of file
133 return false;
134 }
135
136 buf[readBytes] = '\0';
137 if (QByteArray::fromRawData(buf, readBytes).contains("0x"))
138 break;
139 }
140
141 if (outImage->size() != QSize(w, h) || outImage->format() != QImage::Format_MonoLSB) {
142 *outImage = QImage(w, h, QImage::Format_MonoLSB);
143 if (outImage->isNull())
144 return false;
145 }
146
147 outImage->setColorCount(2);
148 outImage->setColor(0, qRgb(255,255,255)); // white
149 outImage->setColor(1, qRgb(0,0,0)); // black
150
151 int x = 0, y = 0;
152 uchar *b = outImage->scanLine(0);
153 char *p = buf + QByteArray::fromRawData(buf, readBytes).indexOf("0x");
154 w = (w+7)/8; // byte width
155
156 while (y < h) { // for all encoded bytes...
157 if (p) { // p = "0x.."
158 *b++ = hex2byte(p+2);
159 p += 2;
160 if (++x == w && ++y < h) {
161 b = outImage->scanLine(y);
162 x = 0;
163 }
164 p = strstr(p, "0x");
165 } else { // read another line
166 if ((readBytes = device->readLine(buf,buflen)) <= 0) // EOF ==> truncated image
167 break;
168 p = buf + QByteArray::fromRawData(buf, readBytes).indexOf("0x");
169 }
170 }
171
172 return true;
173}
174
175static bool read_xbm_image(QIODevice *device, QImage *outImage)
176{
177 int w = 0, h = 0;
178 if (!read_xbm_header(device, w, h))
179 return false;
180 return read_xbm_body(device, w, h, outImage);
181}
182
183static bool write_xbm_image(const QImage &sourceImage, QIODevice *device, const QString &fileName)
184{
185 QImage image = sourceImage;
186 int w = image.width();
187 int h = image.height();
188 int i;
189 QString s = fileName; // get file base name
190 int msize = s.length() + 100;
191 char *buf = new char[msize];
192
193 qsnprintf(buf, msize, "#define %s_width %d\n", s.toAscii().data(), w);
194 device->write(buf, qstrlen(buf));
195 qsnprintf(buf, msize, "#define %s_height %d\n", s.toAscii().data(), h);
196 device->write(buf, qstrlen(buf));
197 qsnprintf(buf, msize, "static char %s_bits[] = {\n ", s.toAscii().data());
198 device->write(buf, qstrlen(buf));
199
200 if (image.format() != QImage::Format_MonoLSB)
201 image = image.convertToFormat(QImage::Format_MonoLSB);
202
203 bool invert = qGray(image.color(0)) < qGray(image.color(1));
204 char hexrep[16];
205 for (i=0; i<10; i++)
206 hexrep[i] = '0' + i;
207 for (i=10; i<16; i++)
208 hexrep[i] = 'a' -10 + i;
209 if (invert) {
210 char t;
211 for (i=0; i<8; i++) {
212 t = hexrep[15-i];
213 hexrep[15-i] = hexrep[i];
214 hexrep[i] = t;
215 }
216 }
217 int bcnt = 0;
218 register char *p = buf;
219 int bpl = (w+7)/8;
220 for (int y = 0; y < h; ++y) {
221 uchar *b = image.scanLine(y);
222 for (i = 0; i < bpl; ++i) {
223 *p++ = '0'; *p++ = 'x';
224 *p++ = hexrep[*b >> 4];
225 *p++ = hexrep[*b++ & 0xf];
226
227 if (i < bpl - 1 || y < h - 1) {
228 *p++ = ',';
229 if (++bcnt > 14) {
230 *p++ = '\n';
231 *p++ = ' ';
232 *p = '\0';
233 if ((int)qstrlen(buf) != device->write(buf, qstrlen(buf))) {
234 delete [] buf;
235 return false;
236 }
237 p = buf;
238 bcnt = 0;
239 }
240 }
241 }
242 }
243#if defined(_MSC_VER) && _MSC_VER >= 1400
244 strcpy_s(p, sizeof(" };\n"), " };\n");
245#else
246 strcpy(p, " };\n");
247#endif
248 if ((int)qstrlen(buf) != device->write(buf, qstrlen(buf))) {
249 delete [] buf;
250 return false;
251 }
252
253 delete [] buf;
254 return true;
255}
256
257QXbmHandler::QXbmHandler()
258 : state(Ready)
259{
260}
261
262bool QXbmHandler::readHeader()
263{
264 state = Error;
265 if (!read_xbm_header(device(), width, height))
266 return false;
267 state = ReadHeader;
268 return true;
269}
270
271bool QXbmHandler::canRead() const
272{
273 if (state == Ready && !canRead(device()))
274 return false;
275
276 if (state != Error) {
277 setFormat("xbm");
278 return true;
279 }
280
281 return false;
282}
283
284bool QXbmHandler::canRead(QIODevice *device)
285{
286 QImage image;
287
288 // it's impossible to tell whether we can load an XBM or not when
289 // it's from a sequential device, as the only way to do it is to
290 // attempt to parse the whole image.
291 if (device->isSequential())
292 return false;
293
294 qint64 oldPos = device->pos();
295 bool success = read_xbm_image(device, &image);
296 device->seek(oldPos);
297
298 return success;
299}
300
301bool QXbmHandler::read(QImage *image)
302{
303 if (state == Error)
304 return false;
305
306 if (state == Ready && !readHeader()) {
307 state = Error;
308 return false;
309 }
310
311 if (!read_xbm_body(device(), width, height, image)) {
312 state = Error;
313 return false;
314 }
315
316 state = Ready;
317 return true;
318}
319
320bool QXbmHandler::write(const QImage &image)
321{
322 return write_xbm_image(image, device(), fileName);
323}
324
325bool QXbmHandler::supportsOption(ImageOption option) const
326{
327 return option == Name
328 || option == Size
329 || option == ImageFormat;
330}
331
332QVariant QXbmHandler::option(ImageOption option) const
333{
334 if (option == Name) {
335 return fileName;
336 } else if (option == Size) {
337 if (state == Error)
338 return QVariant();
339 if (state == Ready && !const_cast<QXbmHandler*>(this)->readHeader())
340 return QVariant();
341 return QSize(width, height);
342 } else if (option == ImageFormat) {
343 return QImage::Format_MonoLSB;
344 }
345 return QVariant();
346}
347
348void QXbmHandler::setOption(ImageOption option, const QVariant &value)
349{
350 if (option == Name)
351 fileName = value.toString();
352}
353
354QByteArray QXbmHandler::name() const
355{
356 return "xbm";
357}
358
359QT_END_NAMESPACE
360
361#endif // QT_NO_IMAGEFORMAT_XBM
Note: See TracBrowser for help on using the repository browser.