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 "private/qbmphandler_p.h"
43
44
#ifndef QT_NO_IMAGEFORMAT_BMP
45
46
#include <qimage.h>
47
#include <qvariant.h>
48
#include <qvector.h>
49
50
QT_BEGIN_NAMESPACE
51
52
static void swapPixel01(QImage *image)        // 1-bpp: swap 0 and 1 pixels
53
{
54
    int i;
55
    if (image->depth() == 1 && image->colorCount() == 2) {
56
        register uint *p = (uint *)image->bits();
57
        int nbytes = image->byteCount();
58
        for (i=0; i<nbytes/4; i++) {
59
            *p = ~*p;
60
            p++;
61
        }
62
        uchar *p2 = (uchar *)p;
63
        for (i=0; i<(nbytes&3); i++) {
64
            *p2 = ~*p2;
65
            p2++;
66
        }
67
        QRgb t = image->color(0);                // swap color 0 and 1
68
        image->setColor(0, image->color(1));
69
        image->setColor(1, t);
70
    }
71
}
72
73
/*
74
    QImageIO::defineIOHandler("BMP", "^BM", 0,
75
                               read_bmp_image, write_bmp_image);
76
*/
77
78
/*****************************************************************************
79
  BMP (DIB) image read/write functions
80
 *****************************************************************************/
81
82
const int BMP_FILEHDR_SIZE = 14;                // size of BMP_FILEHDR data
83
84
static QDataStream &operator>>(QDataStream &s, BMP_FILEHDR &bf)
85
{                                                // read file header
86
    s.readRawData(bf.bfType, 2);
87
    s >> bf.bfSize >> bf.bfReserved1 >> bf.bfReserved2 >> bf.bfOffBits;
88
    return s;
89
}
90
91
static QDataStream &operator<<(QDataStream &s, const BMP_FILEHDR &bf)
92
{                                                // write file header
93
    s.writeRawData(bf.bfType, 2);
94
    s << bf.bfSize << bf.bfReserved1 << bf.bfReserved2 << bf.bfOffBits;
95
    return s;
96
}
97
98
99
const int BMP_OLD  = 12;                        // old Windows/OS2 BMP size
100
const int BMP_WIN  = 40;                        // new Windows BMP size
101
const int BMP_OS2  = 64;                        // new OS/2 BMP size
102
103
const int BMP_RGB  = 0;                                // no compression
104
const int BMP_RLE8 = 1;                                // run-length encoded, 8 bits
105
const int BMP_RLE4 = 2;                                // run-length encoded, 4 bits
106
const int BMP_BITFIELDS = 3;                        // RGB values encoded in data as bit-fields
107
108
109
static QDataStream &operator>>(QDataStream &s, BMP_INFOHDR &bi)
110
{
111
    s >> bi.biSize;
112
    if (bi.biSize == BMP_WIN || bi.biSize == BMP_OS2) {
113
        s >> bi.biWidth >> bi.biHeight >> bi.biPlanes >> bi.biBitCount;
114
        s >> bi.biCompression >> bi.biSizeImage;
115
        s >> bi.biXPelsPerMeter >> bi.biYPelsPerMeter;
116
        s >> bi.biClrUsed >> bi.biClrImportant;
117
    }
118
    else {                                        // probably old Windows format
119
        qint16 w, h;
120
        s >> w >> h >> bi.biPlanes >> bi.biBitCount;
121
        bi.biWidth  = w;
122
        bi.biHeight = h;
123
        bi.biCompression = BMP_RGB;                // no compression
124
        bi.biSizeImage = 0;
125
        bi.biXPelsPerMeter = bi.biYPelsPerMeter = 0;
126
        bi.biClrUsed = bi.biClrImportant = 0;
127
    }
128
    return s;
129
}
130
131
static QDataStream &operator<<(QDataStream &s, const BMP_INFOHDR &bi)
132
{
133
    s << bi.biSize;
134
    s << bi.biWidth << bi.biHeight;
135
    s << bi.biPlanes;
136
    s << bi.biBitCount;
137
    s << bi.biCompression;
138
    s << bi.biSizeImage;
139
    s << bi.biXPelsPerMeter << bi.biYPelsPerMeter;
140
    s << bi.biClrUsed << bi.biClrImportant;
141
    return s;
142
}
143
144
static int calc_shift(int mask)
145
{
146
    int result = 0;
147
    while (mask && !(mask & 1)) {
148
        result++;
149
        mask >>= 1;
150
    }
151
    return result;
152
}
153
154
static bool read_dib_fileheader(QDataStream &s, BMP_FILEHDR &bf)
155
{
156
    // read BMP file header
157
    s >> bf;
158
    if (s.status() != QDataStream::Ok)
159
        return false;
160
161
    // check header
162
    if (qstrncmp(bf.bfType,"BM",2) != 0)
163
        return false;
164
165
    return true;
166
}
167
168
static bool read_dib_infoheader(QDataStream &s, BMP_INFOHDR &bi)
169
{
170
    s >> bi;                                        // read BMP info header
171
    if (s.status() != QDataStream::Ok)
172
        return false;
173
174
    int nbits = bi.biBitCount;
175
    int comp = bi.biCompression;
176
    if (!(nbits == 1 || nbits == 4 || nbits == 8 || nbits == 16 || nbits == 24 || nbits == 32) ||
177
        bi.biPlanes != 1 || comp > BMP_BITFIELDS)
178
        return false;                                        // weird BMP image
179
    if (!(comp == BMP_RGB || (nbits == 4 && comp == BMP_RLE4) ||
180
        (nbits == 8 && comp == BMP_RLE8) || ((nbits == 16 || nbits == 32) && comp == BMP_BITFIELDS)))
181
         return false;                                // weird compression type
182
183
    return true;
184
}
185
186
static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, int offset, int startpos, QImage &image)
187
{
188
    QIODevice* d = s.device();
189
    if (d->atEnd())                                // end of stream/file
190
        return false;
191
#if 0
192
    qDebug("offset...........%d", offset);
193
    qDebug("startpos.........%d", startpos);
194
    qDebug("biSize...........%d", bi.biSize);
195
    qDebug("biWidth..........%d", bi.biWidth);
196
    qDebug("biHeight.........%d", bi.biHeight);
197
    qDebug("biPlanes.........%d", bi.biPlanes);
198
    qDebug("biBitCount.......%d", bi.biBitCount);
199
    qDebug("biCompression....%d", bi.biCompression);
200
    qDebug("biSizeImage......%d", bi.biSizeImage);
201
    qDebug("biXPelsPerMeter..%d", bi.biXPelsPerMeter);
202
    qDebug("biYPelsPerMeter..%d", bi.biYPelsPerMeter);
203
    qDebug("biClrUsed........%d", bi.biClrUsed);
204
    qDebug("biClrImportant...%d", bi.biClrImportant);
205
#endif
206
    int w = bi.biWidth,         h = bi.biHeight,  nbits = bi.biBitCount;
207
    int t = bi.biSize,         comp = bi.biCompression;
208
    int red_mask = 0;
209
    int green_mask = 0;
210
    int blue_mask = 0;
211
    int red_shift = 0;
212
    int green_shift = 0;
213
    int blue_shift = 0;
214
    int red_scale = 0;
215
    int green_scale = 0;
216
    int blue_scale = 0;
217
218
    int ncols = 0;
219
    int depth = 0;
220
    QImage::Format format;
221
    switch (nbits) {
222
        case 32:
223
        case 24:
224
        case 16:
225
            depth = 32;
226
            format = QImage::Format_RGB32;
227
            break;
228
        case 8:
229
        case 4:
230
            depth = 8;
231
            format = QImage::Format_Indexed8;
232
            break;
233
        default:
234
            depth = 1;
235
            format = QImage::Format_Mono;
236
    }
237
238
    if (bi.biHeight < 0)
239
        h = -h;                  // support images with negative height
240
241
    if (image.size() != QSize(w, h) || image.format() != format) {
242
        image = QImage(w, h, format);
243
        if (image.isNull())                        // could not create image
244
            return false;
245
    }
246
247
    if (depth != 32) {
248
        ncols = bi.biClrUsed ? bi.biClrUsed : 1 << nbits;
249
        image.setColorCount(ncols);
250
    }
251
252
    image.setDotsPerMeterX(bi.biXPelsPerMeter);
253
    image.setDotsPerMeterY(bi.biYPelsPerMeter);
254
255
    if (!d->isSequential())
256
        d->seek(startpos + BMP_FILEHDR_SIZE + bi.biSize); // goto start of colormap
257
258
    if (ncols > 0) {                                // read color table
259
        uchar rgb[4];
260
        int   rgb_len = t == BMP_OLD ? 3 : 4;
261
        for (int i=0; i<ncols; i++) {
262
            if (d->read((char *)rgb, rgb_len) != rgb_len)
263
                return false;
264
            image.setColor(i, qRgb(rgb[2],rgb[1],rgb[0]));
265
            if (d->atEnd())                        // truncated file
266
                return false;
267
        }
268
    } else if (comp == BMP_BITFIELDS && (nbits == 16 || nbits == 32)) {
269
        if (d->read((char *)&red_mask, sizeof(red_mask)) != sizeof(red_mask))
270
            return false;
271
        if (d->read((char *)&green_mask, sizeof(green_mask)) != sizeof(green_mask))
272
            return false;
273
        if (d->read((char *)&blue_mask, sizeof(blue_mask)) != sizeof(blue_mask))
274
            return false;
275
        red_shift = calc_shift(red_mask);
276
        red_scale = 256 / ((red_mask >> red_shift) + 1);
277
        green_shift = calc_shift(green_mask);
278
        green_scale = 256 / ((green_mask >> green_shift) + 1);
279
        blue_shift = calc_shift(blue_mask);
280
        blue_scale = 256 / ((blue_mask >> blue_shift) + 1);
281
    } else if (comp == BMP_RGB && (nbits == 24 || nbits == 32)) {
282
        blue_mask = 0x000000ff;
283
        green_mask = 0x0000ff00;
284
        red_mask = 0x00ff0000;
285
        blue_shift = 0;
286
        green_shift = 8;
287
        red_shift = 16;
288
        blue_scale = green_scale = red_scale = 1;
289
    } else if (comp == BMP_RGB && nbits == 16) {
290
        blue_mask = 0x001f;
291
        green_mask = 0x03e0;
292
        red_mask = 0x7c00;
293
        blue_shift = 0;
294
        green_shift = 2;
295
        red_shift = 7;
296
        red_scale = 1;
297
        green_scale = 1;
298
        blue_scale = 8;
299
    }
300
301
    // offset can be bogus, be careful
302
    if (offset>=0 && startpos + offset > d->pos()) {
303
        if (!d->isSequential())
304
            d->seek(startpos + offset);                // start of image data
305
    }
306
307
    int             bpl = image.bytesPerLine();
308
    uchar *data = image.bits();
309
310
    if (nbits == 1) {                                // 1 bit BMP image
311
        while (--h >= 0) {
312
            if (d->read((char*)(data + h*bpl), bpl) != bpl)
313
                break;
314
        }
315
        if (ncols == 2 && qGray(image.color(0)) < qGray(image.color(1)))
316
            swapPixel01(&image);                // pixel 0 is white!
317
    }
318
319
    else if (nbits == 4) {                        // 4 bit BMP image
320
        int    buflen = ((w+7)/8)*4;
321
        uchar *buf    = new uchar[buflen];
322
        if (comp == BMP_RLE4) {                // run length compression
323
            int x=0, y=0, c, i;
324
            quint8 b;
325
            register uchar *p = data + (h-1)*bpl;
326
            const uchar *endp = p + w;
327
            while (y < h) {
328
                if (!d->getChar((char *)&b))
329
                    break;
330
                if (b == 0) {                        // escape code
331
                    if (!d->getChar((char *)&b) || b == 1) {
332
                        y = h;                // exit loop
333
                    } else switch (b) {
334
                        case 0:                        // end of line
335
                            x = 0;
336
                            y++;
337
                            p = data + (h-y-1)*bpl;
338
                            break;
339
                        case 2:                        // delta (jump)
340
                        {
341
                            quint8 tmp;
342
                            d->getChar((char *)&tmp);
343
                            x += tmp;
344
                            d->getChar((char *)&tmp);
345
                            y += tmp;
346
                        }
347
348
                            // Protection
349
                            if ((uint)x >= (uint)w)
350
                                x = w-1;
351
                            if ((uint)y >= (uint)h)
352
                                y = h-1;
353
354
                            p = data + (h-y-1)*bpl + x;
355
                            break;
356
                        default:                // absolute mode
357
                            // Protection
358
                            if (p + b > endp)
359
                                b = endp-p;
360
361
                            i = (c = b)/2;
362
                            while (i--) {
363
                                d->getChar((char *)&b);
364
                                *p++ = b >> 4;
365
                                *p++ = b & 0x0f;
366
                            }
367
                            if (c & 1) {
368
                                unsigned char tmp;
369
                                d->getChar((char *)&tmp);
370
                                *p++ = tmp >> 4;
371
                            }
372
                            if ((((c & 3) + 1) & 2) == 2)
373
                                d->getChar(0);        // align on word boundary
374
                            x += c;
375
                    }
376
                } else {                        // encoded mode
377
                    // Protection
378
                    if (p + b > endp)
379
                        b = endp-p;
380
381
                    i = (c = b)/2;
382
                    d->getChar((char *)&b);                // 2 pixels to be repeated
383
                    while (i--) {
384
                        *p++ = b >> 4;
385
                        *p++ = b & 0x0f;
386
                    }
387
                    if (c & 1)
388
                        *p++ = b >> 4;
389
                    x += c;
390
                }
391
            }
392
        } else if (comp == BMP_RGB) {                // no compression
393
            memset(data, 0, h*bpl);
394
            while (--h >= 0) {
395
                if (d->read((char*)buf,buflen) != buflen)
396
                    break;
397
                register uchar *p = data + h*bpl;
398
                uchar *b = buf;
399
                for (int i=0; i<w/2; i++) {        // convert nibbles to bytes
400
                    *p++ = *b >> 4;
401
                    *p++ = *b++ & 0x0f;
402
                }
403
                if (w & 1)                        // the last nibble
404
                    *p = *b >> 4;
405
            }
406
        }
407
        delete [] buf;
408
    }
409
410
    else if (nbits == 8) {                        // 8 bit BMP image
411
        if (comp == BMP_RLE8) {                // run length compression
412
            int x=0, y=0;
413
            quint8 b;
414
            register uchar *p = data + (h-1)*bpl;
415
            const uchar *endp = p + w;
416
            while (y < h) {
417
                if (!d->getChar((char *)&b))
418
                    break;
419
                if (b == 0) {                        // escape code
420
                    if (!d->getChar((char *)&b) || b == 1) {
421
                            y = h;                // exit loop
422
                    } else switch (b) {
423
                        case 0:                        // end of line
424
                            x = 0;
425
                            y++;
426
                            p = data + (h-y-1)*bpl;
427
                            break;
428
                        case 2:                        // delta (jump)
429
                            // Protection
430
                            if ((uint)x >= (uint)w)
431
                                x = w-1;
432
                            if ((uint)y >= (uint)h)
433
                                y = h-1;
434
435
                            {
436
                                quint8 tmp;
437
                                d->getChar((char *)&tmp);
438
                                x += tmp;
439
                                d->getChar((char *)&tmp);
440
                                y += tmp;
441
                            }
442
                            p = data + (h-y-1)*bpl + x;
443
                            break;
444
                        default:                // absolute mode
445
                            // Protection
446
                            if (p + b > endp)
447
                                b = endp-p;
448
449
                            if (d->read((char *)p, b) != b)
450
                                return false;
451
                            if ((b & 1) == 1)
452
                                d->getChar(0);        // align on word boundary
453
                            x += b;
454
                            p += b;
455
                    }
456
                } else {                        // encoded mode
457
                    // Protection
458
                    if (p + b > endp)
459
                        b = endp-p;
460
461
                    char tmp;
462
                    d->getChar(&tmp);
463
                    memset(p, tmp, b); // repeat pixel
464
                    x += b;
465
                    p += b;
466
                }
467
            }
468
        } else if (comp == BMP_RGB) {                // uncompressed
469
            while (--h >= 0) {
470
                if (d->read((char *)data + h*bpl, bpl) != bpl)
471
                    break;
472
            }
473
        }
474
    }
475
476
    else if (nbits == 16 || nbits == 24 || nbits == 32) { // 16,24,32 bit BMP image
477
        register QRgb *p;
478
        QRgb  *end;
479
        uchar *buf24 = new uchar[bpl];
480
        int    bpl24 = ((w*nbits+31)/32)*4;
481
        uchar *b;
482
        int c;
483
484
        while (--h >= 0) {
485
            p = (QRgb *)(data + h*bpl);
486
            end = p + w;
487
            if (d->read((char *)buf24,bpl24) != bpl24)
488
                break;
489
            b = buf24;
490
            while (p < end) {
491
                c = *(uchar*)b | (*(uchar*)(b+1)<<8);
492
                if (nbits != 16)
493
                    c |= *(uchar*)(b+2)<<16;
494
                *p++ = qRgb(((c & red_mask) >> red_shift) * red_scale,
495
                                        ((c & green_mask) >> green_shift) * green_scale,
496
                                        ((c & blue_mask) >> blue_shift) * blue_scale);
497
                b += nbits/8;
498
            }
499
        }
500
        delete[] buf24;
501
    }
502
503
    if (bi.biHeight < 0) {
504
        // Flip the image
505
        uchar *buf = new uchar[bpl];
506
        h = -bi.biHeight;
507
        for (int y = 0; y < h/2; ++y) {
508
            memcpy(buf, data + y*bpl, bpl);
509
            memcpy(data + y*bpl, data + (h-y-1)*bpl, bpl);
510
            memcpy(data + (h-y-1)*bpl, buf, bpl);
511
        }
512
        delete [] buf;
513
    }
514
515
    return true;
516
}
517
518
// this is also used in qmime_win.cpp
519
bool qt_write_dib(QDataStream &s, QImage image)
520
{
521
    int        nbits;
522
    int        bpl_bmp;
523
    int        bpl = image.bytesPerLine();
524
525
    QIODevice* d = s.device();
526
    if (!d->isWritable())
527
        return false;
528
529
    if (image.depth() == 8 && image.colorCount() <= 16) {
530
        bpl_bmp = (((bpl+1)/2+3)/4)*4;
531
        nbits = 4;
532
    } else if (image.depth() == 32) {
533
        bpl_bmp = ((image.width()*24+31)/32)*4;
534
        nbits = 24;
535
#ifdef Q_WS_QWS
536
    } else if (image.depth() == 1 || image.depth() == 8) {
537
        // Qt for Embedded Linux doesn't word align.
538
        bpl_bmp = ((image.width()*image.depth()+31)/32)*4;
539
        nbits = image.depth();
540
#endif
541
    } else {
542
        bpl_bmp = bpl;
543
        nbits = image.depth();
544
    }
545
546
    BMP_INFOHDR bi;
547
    bi.biSize               = BMP_WIN;                // build info header
548
    bi.biWidth               = image.width();
549
    bi.biHeight               = image.height();
550
    bi.biPlanes               = 1;
551
    bi.biBitCount      = nbits;
552
    bi.biCompression   = BMP_RGB;
553
    bi.biSizeImage     = bpl_bmp*image.height();
554
    bi.biXPelsPerMeter = image.dotsPerMeterX() ? image.dotsPerMeterX()
555
                                                : 2834; // 72 dpi default
556
    bi.biYPelsPerMeter = image.dotsPerMeterY() ? image.dotsPerMeterY() : 2834;
557
    bi.biClrUsed       = image.colorCount();
558
    bi.biClrImportant  = image.colorCount();
559
    s << bi;                                        // write info header
560
    if (s.status() != QDataStream::Ok)
561
        return false;
562
563
    if (image.depth() != 32) {                // write color table
564
        uchar *color_table = new uchar[4*image.colorCount()];
565
        uchar *rgb = color_table;
566
        QVector<QRgb> c = image.colorTable();
567
        for (int i=0; i<image.colorCount(); i++) {
568
            *rgb++ = qBlue (c[i]);
569
            *rgb++ = qGreen(c[i]);
570
            *rgb++ = qRed  (c[i]);
571
            *rgb++ = 0;
572
        }
573
        if (d->write((char *)color_table, 4*image.colorCount()) == -1) {
574
            delete [] color_table;
575
            return false;
576
        }
577
        delete [] color_table;
578
    }
579
580
    if (image.format() == QImage::Format_MonoLSB)
581
        image = image.convertToFormat(QImage::Format_Mono);
582
583
    int y;
584
585
    if (nbits == 1 || nbits == 8) {                // direct output
586
#ifdef Q_WS_QWS
587
        // Qt for Embedded Linux doesn't word align.
588
        int pad = bpl_bmp - bpl;
589
        char padding[4];
590
#endif
591
        for (y=image.height()-1; y>=0; y--) {
592
            if (d->write((char*)image.scanLine(y), bpl) == -1)
593
                return false;
594
#ifdef Q_WS_QWS
595
            if (d->write(padding, pad) == -1)
596
                return false;
597
#endif
598
        }
599
        return true;
600
    }
601
602
    uchar *buf        = new uchar[bpl_bmp];
603
    uchar *b, *end;
604
    register uchar *p;
605
606
    memset(buf, 0, bpl_bmp);
607
    for (y=image.height()-1; y>=0; y--) {        // write the image bits
608
        if (nbits == 4) {                        // convert 8 -> 4 bits
609
            p = image.scanLine(y);
610
            b = buf;
611
            end = b + image.width()/2;
612
            while (b < end) {
613
                *b++ = (*p << 4) | (*(p+1) & 0x0f);
614
                p += 2;
615
            }
616
            if (image.width() & 1)
617
                *b = *p << 4;
618
        } else {                                // 32 bits
619
            QRgb *p   = (QRgb *)image.scanLine(y);
620
            QRgb *end = p + image.width();
621
            b = buf;
622
            while (p < end) {
623
                *b++ = qBlue(*p);
624
                *b++ = qGreen(*p);
625
                *b++ = qRed(*p);
626
                p++;
627
            }
628
        }
629
        if (bpl_bmp != d->write((char*)buf, bpl_bmp)) {
630
            delete[] buf;
631
            return false;
632
        }
633
    }
634
    delete[] buf;
635
    return true;
636
}
637
638
// this is also used in qmime_win.cpp
639
bool qt_read_dib(QDataStream &s, QImage &image)
640
{
641
    BMP_INFOHDR bi;
642
    if (!read_dib_infoheader(s, bi))
643
        return false;
644
    return read_dib_body(s, bi, -1, -BMP_FILEHDR_SIZE, image);
645
}
646
647
QBmpHandler::QBmpHandler()
648
    : state(Ready)
649
{
650
}
651
652
bool QBmpHandler::readHeader()
653
{
654
    state = Error;
655
656
    QIODevice *d = device();
657
    QDataStream s(d);
658
    startpos = d->pos();
659
660
    // Intel byte order
661
    s.setByteOrder(QDataStream::LittleEndian);
662
663
    // read BMP file header
664
    if (!read_dib_fileheader(s, fileHeader))
665
        return false;
666
667
    // read BMP info header
668
    if (!read_dib_infoheader(s, infoHeader))
669
        return false;
670
671
    state = ReadHeader;
672
    return true;
673
}
674
675
bool QBmpHandler::canRead() const
676
{
677
    if (state == Ready) {
678
        if (!canRead(device()))
679
            return false;
680
        setFormat("bmp");
681
        return true;
682
    }
683
    return state != Error;
684
}
685
686
bool QBmpHandler::canRead(QIODevice *device)
687
{
688
    if (!device) {
689
        qWarning("QBmpHandler::canRead() called with 0 pointer");
690
        return false;
691
    }
692
693
    char head[2];
694
    if (device->peek(head, sizeof(head)) != sizeof(head))
695
        return false;
696
697
    return (qstrncmp(head, "BM", 2) == 0);
698
}
699
700
bool QBmpHandler::read(QImage *image)
701
{
702
    if (state == Error)
703
        return false;
704
705
    if (!image) {
706
        qWarning("QBmpHandler::read: cannot read into null pointer");
707
        return false;
708
    }
709
710
    if (state == Ready && !readHeader()) {
711
        state = Error;
712
        return false;
713
    }
714
715
    QIODevice *d = device();
716
    QDataStream s(d);
717
718
    // Intel byte order
719
    s.setByteOrder(QDataStream::LittleEndian);
720
721
    // read image
722
    if (!read_dib_body(s, infoHeader, fileHeader.bfOffBits, startpos, *image))
723
        return false;
724
725
    state = Ready;
726
    return true;
727
}
728
729
bool QBmpHandler::write(const QImage &img)
730
{
731
    QImage image;
732
    switch (img.format()) {
733
    case QImage::Format_ARGB8565_Premultiplied:
734
    case QImage::Format_ARGB8555_Premultiplied:
735
    case QImage::Format_ARGB6666_Premultiplied:
736
    case QImage::Format_ARGB4444_Premultiplied:
737
        image = img.convertToFormat(QImage::Format_ARGB32);
738
        break;
739
    case QImage::Format_RGB16:
740
    case QImage::Format_RGB888:
741
    case QImage::Format_RGB666:
742
    case QImage::Format_RGB555:
743
    case QImage::Format_RGB444:
744
        image = img.convertToFormat(QImage::Format_RGB32);
745
        break;
746
    default:
747
        image = img;
748
    }
749
750
    QIODevice *d = device();
751
    QDataStream s(d);
752
    BMP_FILEHDR bf;
753
    int bpl_bmp;
754
    int bpl = image.bytesPerLine();
755
756
    // Code partially repeated in qt_write_dib
757
    if (image.depth() == 8 && image.colorCount() <= 16) {
758
        bpl_bmp = (((bpl+1)/2+3)/4)*4;
759
    } else if (image.depth() == 32) {
760
        bpl_bmp = ((image.width()*24+31)/32)*4;
761
    } else {
762
        bpl_bmp = bpl;
763
    }
764
765
    // Intel byte order
766
    s.setByteOrder(QDataStream::LittleEndian);
767
768
    // build file header
769
    memcpy(bf.bfType, "BM", 2);
770
771
    // write file header
772
    bf.bfReserved1 = 0;
773
    bf.bfReserved2 = 0;
774
    bf.bfOffBits = BMP_FILEHDR_SIZE + BMP_WIN + image.colorCount() * 4;
775
    bf.bfSize = bf.bfOffBits + bpl_bmp*image.height();
776
    s << bf;
777
778
    // write image
779
    return qt_write_dib(s, image);
780
}
781
782
bool QBmpHandler::supportsOption(ImageOption option) const
783
{
784
    return option == Size
785
            || option == ImageFormat;
786
}
787
788
QVariant QBmpHandler::option(ImageOption option) const
789
{
790
    if (option == Size) {
791
        if (state == Error)
792
            return QVariant();
793
        if (state == Ready && !const_cast<QBmpHandler*>(this)->readHeader())
794
            return QVariant();
795
        return QSize(infoHeader.biWidth, infoHeader.biHeight);
796
    } else if (option == ImageFormat) {
797
        if (state == Error)
798
            return QVariant();
799
        if (state == Ready && !const_cast<QBmpHandler*>(this)->readHeader())
800
            return QVariant();
801
        QImage::Format format;
802
        switch (infoHeader.biBitCount) {
803
            case 32:
804
            case 24:
805
            case 16:
806
                format = QImage::Format_RGB32;
807
                break;
808
            case 8:
809
            case 4:
810
                format = QImage::Format_Indexed8;
811
                break;
812
            default:
813
                format = QImage::Format_Mono;
814
            }
815
        return format;
816
    }
817
    return QVariant();
818
}
819
820
void QBmpHandler::setOption(ImageOption option, const QVariant &value)
821
{
822
    Q_UNUSED(option);
823
    Q_UNUSED(value);
824
}
825
826
QByteArray QBmpHandler::name() const
827
{
828
    return "bmp";
829
}
830
831
QT_END_NAMESPACE
832
833
#endif // QT_NO_IMAGEFORMAT_BMP