1
/****************************************************************************
2
**
3
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4
** All rights reserved.
5
** Contact: Nokia Corporation (qt-info@nokia.com)
6
**
7
** This file is part of the QtGui module of the Qt Toolkit.
8
**
9
** $QT_BEGIN_LICENSE:LGPL$
10
** No Commercial Usage
11
** This file contains pre-release code and may not be distributed.
12
** You may use this file in accordance with the terms and conditions
13
** contained in the Technology Preview License Agreement accompanying
14
** this package.
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
** If you have questions regarding the use of this file, please contact
29
** Nokia at qt-info@nokia.com.
30
**
31
**
32
**
33
**
34
**
35
**
36
**
37
**
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
54
QT_BEGIN_NAMESPACE
55
56
/*****************************************************************************
57
  X bitmap image read/write functions
58
 *****************************************************************************/
59
60
static 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
66
static 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
113
static 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->setColorCount(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
166
static 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
174
static 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
248
QXbmHandler::QXbmHandler()
249
    : state(Ready)
250
{
251
}
252
253
bool 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
262
bool 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
273
bool 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
290
bool 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
309
bool QXbmHandler::write(const QImage &image)
310
{
311
    return write_xbm_image(image, device(), fileName);
312
}
313
314
bool QXbmHandler::supportsOption(ImageOption option) const
315
{
316
    return option == Name
317
        || option == Size
318
        || option == ImageFormat;
319
}
320
321
QVariant 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
337
void QXbmHandler::setOption(ImageOption option, const QVariant &value)
338
{
339
    if (option == Name)
340
        fileName = value.toString();
341
}
342
343
QByteArray QXbmHandler::name() const
344
{
345
    return "xbm";
346
}
347
348
QT_END_NAMESPACE
349
350
#endif // QT_NO_IMAGEFORMAT_XBM