1
/****************************************************************************
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
5
** This file is part of the painting module of the Qt Toolkit.
7
** This file may be distributed under the terms of the Q Public License
8
** as defined by Trolltech AS of Norway and appearing in the file
9
** LICENSE.QPL included in the packaging of this file.
11
** This file may be distributed and/or modified under the terms of the
12
** GNU General Public License version 2 as published by the Free Software
13
** Foundation and appearing in the file LICENSE.GPL included in the
14
** packaging of this file.
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
17
** information about Qt Commercial License Agreements.
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
21
** Contact info@trolltech.com if any conditions of this licensing are
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27
****************************************************************************/
29
#include "private/qbmphandler_p.h"
34
static void swapPixel01(QImage *image) // 1-bpp: swap 0 and 1 pixels
37
if (image->depth() == 1 && image->numColors() == 2) {
38
register uint *p = (uint *)image->bits();
39
int nbytes = image->numBytes();
40
for (i=0; i<nbytes/4; i++) {
44
uchar *p2 = (uchar *)p;
45
for (i=0; i<(nbytes&3); i++) {
49
QRgb t = image->color(0); // swap color 0 and 1
50
image->setColor(0, image->color(1));
51
image->setColor(1, t);
56
QImageIO::defineIOHandler("BMP", "^BM", 0,
57
read_bmp_image, write_bmp_image);
60
/*****************************************************************************
61
BMP (DIB) image read/write functions
62
*****************************************************************************/
64
const int BMP_FILEHDR_SIZE = 14; // size of BMP_FILEHDR data
66
struct BMP_FILEHDR { // BMP file header
67
char bfType[2]; // "BM"
68
qint32 bfSize; // size of file
71
qint32 bfOffBits; // pointer to the pixmap bits
74
QDataStream &operator>>(QDataStream &s, BMP_FILEHDR &bf)
76
s.readRawData(bf.bfType, 2);
77
s >> bf.bfSize >> bf.bfReserved1 >> bf.bfReserved2 >> bf.bfOffBits;
81
QDataStream &operator<<(QDataStream &s, const BMP_FILEHDR &bf)
82
{ // write file header
83
s.writeRawData(bf.bfType, 2);
84
s << bf.bfSize << bf.bfReserved1 << bf.bfReserved2 << bf.bfOffBits;
89
const int BMP_OLD = 12; // old Windows/OS2 BMP size
90
const int BMP_WIN = 40; // new Windows BMP size
91
const int BMP_OS2 = 64; // new OS/2 BMP size
93
const int BMP_RGB = 0; // no compression
94
const int BMP_RLE8 = 1; // run-length encoded, 8 bits
95
const int BMP_RLE4 = 2; // run-length encoded, 4 bits
96
const int BMP_BITFIELDS = 3; // RGB values encoded in data as bit-fields
98
struct BMP_INFOHDR { // BMP information header
99
qint32 biSize; // size of this struct
100
qint32 biWidth; // pixmap width
101
qint32 biHeight; // pixmap height
102
qint16 biPlanes; // should be 1
103
qint16 biBitCount; // number of bits per pixel
104
qint32 biCompression; // compression method
105
qint32 biSizeImage; // size of image
106
qint32 biXPelsPerMeter; // horizontal resolution
107
qint32 biYPelsPerMeter; // vertical resolution
108
qint32 biClrUsed; // number of colors used
109
qint32 biClrImportant; // number of important colors
113
QDataStream &operator>>(QDataStream &s, BMP_INFOHDR &bi)
116
if (bi.biSize == BMP_WIN || bi.biSize == BMP_OS2) {
117
s >> bi.biWidth >> bi.biHeight >> bi.biPlanes >> bi.biBitCount;
118
s >> bi.biCompression >> bi.biSizeImage;
119
s >> bi.biXPelsPerMeter >> bi.biYPelsPerMeter;
120
s >> bi.biClrUsed >> bi.biClrImportant;
122
else { // probably old Windows format
124
s >> w >> h >> bi.biPlanes >> bi.biBitCount;
127
bi.biCompression = BMP_RGB; // no compression
129
bi.biXPelsPerMeter = bi.biYPelsPerMeter = 0;
130
bi.biClrUsed = bi.biClrImportant = 0;
135
QDataStream &operator<<(QDataStream &s, const BMP_INFOHDR &bi)
138
s << bi.biWidth << bi.biHeight;
141
s << bi.biCompression;
143
s << bi.biXPelsPerMeter << bi.biYPelsPerMeter;
144
s << bi.biClrUsed << bi.biClrImportant;
148
static int calc_shift(int mask)
151
while (!(mask & 1)) {
158
static bool read_dib(QDataStream &s, int offset, int startpos, QImage &image)
161
QIODevice* d = s.device();
163
s >> bi; // read BMP info header
164
if (d->atEnd()) // end of stream/file
167
qDebug("offset...........%d", offset);
168
qDebug("startpos.........%d", startpos);
169
qDebug("biSize...........%d", bi.biSize);
170
qDebug("biWidth..........%d", bi.biWidth);
171
qDebug("biHeight.........%d", bi.biHeight);
172
qDebug("biPlanes.........%d", bi.biPlanes);
173
qDebug("biBitCount.......%d", bi.biBitCount);
174
qDebug("biCompression....%d", bi.biCompression);
175
qDebug("biSizeImage......%d", bi.biSizeImage);
176
qDebug("biXPelsPerMeter..%d", bi.biXPelsPerMeter);
177
qDebug("biYPelsPerMeter..%d", bi.biYPelsPerMeter);
178
qDebug("biClrUsed........%d", bi.biClrUsed);
179
qDebug("biClrImportant...%d", bi.biClrImportant);
181
int w = bi.biWidth, h = bi.biHeight, nbits = bi.biBitCount;
182
int t = bi.biSize, comp = bi.biCompression;
183
int red_mask, green_mask, blue_mask;
191
if (!(nbits == 1 || nbits == 4 || nbits == 8 || nbits == 16 || nbits == 24 || nbits == 32) ||
192
bi.biPlanes != 1 || comp > BMP_BITFIELDS)
193
return false; // weird BMP image
194
if (!(comp == BMP_RGB || (nbits == 4 && comp == BMP_RLE4) ||
195
(nbits == 8 && comp == BMP_RLE8) || ((nbits == 16 || nbits == 32) && comp == BMP_BITFIELDS)))
196
return false; // weird compression type
200
QImage::Format format;
206
format = QImage::Format_RGB32;
211
format = QImage::Format_Indexed8;
215
format = QImage::Format_Mono;
218
image = QImage(w, h, format);
219
if (image.isNull()) // could not create image
223
ncols = bi.biClrUsed ? bi.biClrUsed : 1 << nbits;
224
image.setNumColors(ncols);
227
image.setDotsPerMeterX(bi.biXPelsPerMeter);
228
image.setDotsPerMeterY(bi.biYPelsPerMeter);
230
d->seek(startpos + BMP_FILEHDR_SIZE + bi.biSize); // goto start of colormap
232
if (ncols > 0) { // read color table
234
int rgb_len = t == BMP_OLD ? 3 : 4;
235
for (int i=0; i<ncols; i++) {
236
if (d->read((char *)rgb, rgb_len) != rgb_len)
238
image.setColor(i, qRgb(rgb[2],rgb[1],rgb[0]));
239
if (d->atEnd()) // truncated file
242
} else if (comp == BMP_BITFIELDS && (nbits == 16 || nbits == 32)) {
243
if (d->read((char *)&red_mask, sizeof(red_mask)) != sizeof(red_mask))
245
if (d->read((char *)&green_mask, sizeof(green_mask)) != sizeof(green_mask))
247
if (d->read((char *)&blue_mask, sizeof(blue_mask)) != sizeof(blue_mask))
249
red_shift = calc_shift(red_mask);
250
red_scale = 256 / ((red_mask >> red_shift) + 1);
251
green_shift = calc_shift(green_mask);
252
green_scale = 256 / ((green_mask >> green_shift) + 1);
253
blue_shift = calc_shift(blue_mask);
254
blue_scale = 256 / ((blue_mask >> blue_shift) + 1);
255
} else if (comp == BMP_RGB && (nbits == 24 || nbits == 32)) {
256
blue_mask = 0x000000ff;
257
green_mask = 0x0000ff00;
258
red_mask = 0x00ff0000;
262
blue_scale = green_scale = red_scale = 1;
263
} else if (comp == BMP_RGB && nbits == 16) // don't support RGB values for 15/16 bpp
266
// offset can be bogus, be careful
267
if (offset>=0 && startpos + offset > d->pos())
268
d->seek(startpos + offset); // start of image data
270
int bpl = image.bytesPerLine();
273
// Guess the number of bytes-per-line if we don't know how much
274
// image data is in the file (bogus image ?).
276
int bmpbpl = bi.biSizeImage > 0 ?
277
bi.biSizeImage / bi.biHeight :
278
(d->size() - offset) / bi.biHeight;
279
int pad = bmpbpl-bpl;
281
uchar *data = image.bits();
283
if (nbits == 1) { // 1 bit BMP image
285
if (d->read((char*)(data + h*bpl), bpl) != bpl)
289
d->seek(d->pos()+pad);
292
if (ncols == 2 && qGray(image.color(0)) < qGray(image.color(1)))
293
swapPixel01(&image); // pixel 0 is white!
296
else if (nbits == 4) { // 4 bit BMP image
297
int buflen = ((w+7)/8)*4;
298
uchar *buf = new uchar[buflen];
299
if (comp == BMP_RLE4) { // run length compression
302
register uchar *p = data + (h-1)*bpl;
303
const uchar *endp = p + w;
307
if (b == 0) { // escape code
308
if (!d->getChar(&b) || b == 1) {
311
case 0: // end of line
314
p = data + (h-y-1)*bpl;
316
case 2: // delta (jump)
326
if ((uint)x >= (uint)w)
328
if ((uint)y >= (uint)h)
331
p = data + (h-y-1)*bpl + x;
333
default: // absolute mode
349
if ((((c & 3) + 1) & 2) == 2)
350
d->getChar(0); // align on word boundary
353
} else { // encoded mode
359
d->getChar(&b); // 2 pixels to be repeated
369
} else if (comp == BMP_RGB) { // no compression
371
if (d->read((char*)buf,buflen) != buflen)
373
register uchar *p = data + h*bpl;
375
for (int i=0; i<w/2; i++) { // convert nibbles to bytes
379
if (w & 1) // the last nibble
386
else if (nbits == 8) { // 8 bit BMP image
387
if (comp == BMP_RLE8) { // run length compression
390
register uchar *p = data + (h-1)*bpl;
391
const uchar *endp = p + w;
393
if (!d->getChar((char *)&b))
395
if (b == 0) { // escape code
396
if (!d->getChar((char *)&b) || b == 1) {
399
case 0: // end of line
402
p = data + (h-y-1)*bpl;
404
case 2: // delta (jump)
406
if ((uint)x >= (uint)w)
408
if ((uint)y >= (uint)h)
418
p = data + (h-y-1)*bpl + x;
420
default: // absolute mode
425
if (d->read((char *)p, b) != b)
428
d->getChar(0); // align on word boundary
432
} else { // encoded mode
439
memset(p, tmp, b); // repeat pixel
444
} else if (comp == BMP_RGB) { // uncompressed
446
if (d->read((char *)data + h*bpl, bpl) != bpl)
450
d->seek(d->pos()+pad);
456
else if (nbits == 16 || nbits == 24 || nbits == 32) { // 16,24,32 bit BMP image
459
uchar *buf24 = new uchar[bpl];
460
int bpl24 = ((w*nbits+31)/32)*4;
465
p = (QRgb *)(data + h*bpl);
467
if (d->read((char *)buf24,bpl24) != bpl24)
471
c = *(uchar*)b | (*(uchar*)(b+1)<<8);
473
c |= *(uchar*)(b+2)<<16;
474
*p++ = qRgb(((c & red_mask) >> red_shift) * red_scale,
475
((c & green_mask) >> green_shift) * green_scale,
476
((c & blue_mask) >> blue_shift) * blue_scale);
486
// this is also used in qmime_win.cpp
487
bool Q_GUI_EXPORT qt_write_dib(QDataStream &s, QImage image)
491
int bpl = image.bytesPerLine();
493
QIODevice* d = s.device();
495
if (image.depth() == 8 && image.numColors() <= 16) {
496
bpl_bmp = (((bpl+1)/2+3)/4)*4;
498
} else if (image.depth() == 32) {
499
bpl_bmp = ((image.width()*24+31)/32)*4;
502
} else if (image.depth() == 1 || image.depth() == 8) {
503
// Qt/E doesn't word align.
504
bpl_bmp = ((image.width()*image.depth()+31)/32)*4;
505
nbits = image.depth();
509
nbits = image.depth();
513
bi.biSize = BMP_WIN; // build info header
514
bi.biWidth = image.width();
515
bi.biHeight = image.height();
517
bi.biBitCount = nbits;
518
bi.biCompression = BMP_RGB;
519
bi.biSizeImage = bpl_bmp*image.height();
520
bi.biXPelsPerMeter = image.dotsPerMeterX() ? image.dotsPerMeterX()
521
: 2834; // 72 dpi default
522
bi.biYPelsPerMeter = image.dotsPerMeterY() ? image.dotsPerMeterY() : 2834;
523
bi.biClrUsed = image.numColors();
524
bi.biClrImportant = image.numColors();
525
s << bi; // write info header
527
if (image.depth() != 32) { // write color table
528
uchar *color_table = new uchar[4*image.numColors()];
529
uchar *rgb = color_table;
530
QVector<QRgb> c = image.colorTable();
531
for (int i=0; i<image.numColors(); i++) {
532
*rgb++ = qBlue (c[i]);
533
*rgb++ = qGreen(c[i]);
534
*rgb++ = qRed (c[i]);
537
d->write((char *)color_table, 4*image.numColors());
538
delete [] color_table;
541
if (image.format() == QImage::Format_MonoLSB)
542
image = image.convertToFormat(QImage::Format_Mono);
546
if (nbits == 1 || nbits == 8) { // direct output
548
// Qt/E doesn't word align.
549
int pad = bpl_bmp - bpl;
552
for (y=image.height()-1; y>=0; y--) {
553
d->write((char*)image.scanLine(y), bpl);
555
d->write(padding, pad);
561
uchar *buf = new uchar[bpl_bmp];
565
memset(buf, 0, bpl_bmp);
566
for (y=image.height()-1; y>=0; y--) { // write the image bits
567
if (nbits == 4) { // convert 8 -> 4 bits
568
p = image.scanLine(y);
570
end = b + image.width()/2;
572
*b++ = (*p << 4) | (*(p+1) & 0x0f);
575
if (image.width() & 1)
578
QRgb *p = (QRgb *)image.scanLine(y);
579
QRgb *end = p + image.width();
588
if (bpl_bmp != d->write((char*)buf, bpl_bmp)) {
597
// this is also used in qmime_win.cpp
598
bool Q_GUI_EXPORT qt_read_dib(QDataStream &s, QImage &image)
600
return read_dib(s,-1,-BMP_FILEHDR_SIZE,image);
603
bool QBmpHandler::canRead() const
605
return canRead(device());
608
bool QBmpHandler::canRead(QIODevice *device)
611
qWarning("QBmpHandler::canRead() called with 0 pointer");
615
qint64 oldPos = device->pos();
618
qint64 readBytes = device->read(head, sizeof(head));
619
if (readBytes != sizeof(head)) {
620
if (device->isSequential()) {
621
while (readBytes > 0)
622
device->ungetChar(head[readBytes-- - 1]);
624
device->seek(oldPos);
629
if (device->isSequential()) {
630
while (readBytes > 0)
631
device->ungetChar(head[readBytes-- - 1]);
633
device->seek(oldPos);
636
return (qstrncmp(head, "BM", 2) == 0);
639
bool QBmpHandler::read(QImage *image)
641
QIODevice *d = device();
643
BMP_FILEHDR bf = {{0, 0}, 0, 0, 0, 0};
644
int startpos = d->pos();
647
s.setByteOrder(QDataStream::LittleEndian);
649
// read BMP file header
653
if (qstrncmp(bf.bfType,"BM",2) != 0)
658
if (!read_dib(s, bf.bfOffBits, startpos, tmpImage))
666
bool QBmpHandler::write(const QImage &image)
668
QIODevice *d = device();
672
int bpl = image.bytesPerLine();
674
// Code partially repeated in qt_write_dib
675
if (image.depth() == 8 && image.numColors() <= 16) {
676
bpl_bmp = (((bpl+1)/2+3)/4)*4;
677
} else if (image.depth() == 32) {
678
bpl_bmp = ((image.width()*24+31)/32)*4;
684
s.setByteOrder(QDataStream::LittleEndian);
687
strncpy(bf.bfType, "BM", 2);
692
bf.bfOffBits = BMP_FILEHDR_SIZE + BMP_WIN + image.numColors() * 4;
693
bf.bfSize = bf.bfOffBits + bpl_bmp*image.height();
697
return qt_write_dib(s, image);
700
QByteArray QBmpHandler::name() const