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

Last change on this file since 106 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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