Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qbmphandler.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "private/qbmphandler_p.h"
5
6#ifndef QT_NO_IMAGEFORMAT_BMP
7
8#include <qimage.h>
9#include <qlist.h>
10#include <qvariant.h>
11
13
14static void swapPixel01(QImage *image) // 1-bpp: swap 0 and 1 pixels
15{
17 if (image->depth() == 1 && image->colorCount() == 2) {
18 uint *p = (uint *)image->bits();
19 qsizetype nbytes = static_cast<qsizetype>(image->sizeInBytes());
20 for (i=0; i<nbytes/4; i++) {
21 *p = ~*p;
22 p++;
23 }
24 uchar *p2 = (uchar *)p;
25 for (i=0; i<(nbytes&3); i++) {
26 *p2 = ~*p2;
27 p2++;
28 }
29 QRgb t = image->color(0); // swap color 0 and 1
30 image->setColor(0, image->color(1));
31 image->setColor(1, t);
32 }
33}
34
35/*
36 QImageIO::defineIOHandler("BMP", "^BM", 0,
37 read_bmp_image, write_bmp_image);
38*/
39
40/*****************************************************************************
41 BMP (DIB) image read/write functions
42 *****************************************************************************/
43
44const int BMP_FILEHDR_SIZE = 14; // size of BMP_FILEHDR data
45
47{ // read file header
48 s.readRawData(bf.bfType, 2);
49 s >> bf.bfSize >> bf.bfReserved1 >> bf.bfReserved2 >> bf.bfOffBits;
50 return s;
51}
52
54{ // write file header
55 s.writeRawData(bf.bfType, 2);
56 s << bf.bfSize << bf.bfReserved1 << bf.bfReserved2 << bf.bfOffBits;
57 return s;
58}
59
60
61const int BMP_OLD = 12; // old Windows/OS2 BMP size
62const int BMP_WIN = 40; // Windows BMP v3 size
63const int BMP_OS2 = 64; // new OS/2 BMP size
64const int BMP_WIN4 = 108; // Windows BMP v4 size
65const int BMP_WIN5 = 124; // Windows BMP v5 size
66
67const int BMP_RGB = 0; // no compression
68const int BMP_RLE8 = 1; // run-length encoded, 8 bits
69const int BMP_RLE4 = 2; // run-length encoded, 4 bits
70const int BMP_BITFIELDS = 3; // RGB values encoded in data as bit-fields
71const int BMP_ALPHABITFIELDS = 4; // RGBA values encoded in data as bit-fields
72
73
75{
76 s >> bi.biSize;
77 if (bi.biSize == BMP_WIN || bi.biSize == BMP_OS2 || bi.biSize == BMP_WIN4 || bi.biSize == BMP_WIN5) {
78 s >> bi.biWidth >> bi.biHeight >> bi.biPlanes >> bi.biBitCount;
79 s >> bi.biCompression >> bi.biSizeImage;
81 s >> bi.biClrUsed >> bi.biClrImportant;
82 if (bi.biSize >= BMP_WIN4) {
83 s >> bi.biRedMask >> bi.biGreenMask >> bi.biBlueMask >> bi.biAlphaMask;
84 s >> bi.biCSType;
85 for (int i = 0; i < 9; ++i)
86 s >> bi.biEndpoints[i];
87 s >> bi.biGammaRed >> bi.biGammaGreen >> bi.biGammaBlue;
88 if (bi.biSize == BMP_WIN5)
89 s >> bi.biIntent >> bi.biProfileData >> bi.biProfileSize >> bi.biReserved;
90 }
91 }
92 else { // probably old Windows format
93 qint16 w, h;
94 s >> w >> h >> bi.biPlanes >> bi.biBitCount;
95 bi.biWidth = w;
96 bi.biHeight = h;
97 bi.biCompression = BMP_RGB; // no compression
98 bi.biSizeImage = 0;
100 bi.biClrUsed = bi.biClrImportant = 0;
101 }
102 return s;
103}
104
106{
107 s << bi.biSize;
108 s << bi.biWidth << bi.biHeight;
109 s << bi.biPlanes;
110 s << bi.biBitCount;
111 s << bi.biCompression;
112 s << bi.biSizeImage;
114 s << bi.biClrUsed << bi.biClrImportant;
115
116 if (bi.biSize >= BMP_WIN4) {
117 s << bi.biRedMask << bi.biGreenMask << bi.biBlueMask << bi.biAlphaMask;
118 s << bi.biCSType;
119
120 for (int i = 0; i < 9; i++)
121 s << bi.biEndpoints[i];
122
123 s << bi.biGammaRed;
124 s << bi.biGammaGreen;
125 s << bi.biGammaBlue;
126 }
127
128 if (bi.biSize >= BMP_WIN5) {
129 s << bi.biIntent;
130 s << bi.biProfileData;
131 s << bi.biProfileSize;
132 s << bi.biReserved;
133 }
134
135 return s;
136}
137
139{
140 uint result = 0;
141 while ((mask >= 0x100) || (!(mask & 1) && mask)) {
142 result++;
143 mask >>= 1;
144 }
145 return result;
146}
147
148static uint calc_scale(uint low_mask)
149{
150 uint result = 8;
151 while (low_mask && result) {
152 result--;
153 low_mask >>= 1;
154 }
155 return result;
156}
157
159{
160 if (!(scale & 0x07)) // return immediately if scale == 8 or 0
161 return value;
162
163 uint filled = 8 - scale;
164 uint result = value << scale;
165
166 do {
167 result |= result >> filled;
168 filled <<= 1;
169 } while (filled < 8);
170
171 return result;
172}
173
175{
176 // read BMP file header
177 s >> bf;
178 if (s.status() != QDataStream::Ok)
179 return false;
180
181 // check header
182 if (qstrncmp(bf.bfType,"BM",2) != 0)
183 return false;
184
185 return true;
186}
187
189{
190 s >> bi; // read BMP info header
191 if (s.status() != QDataStream::Ok)
192 return false;
193
194 int nbits = bi.biBitCount;
195 int comp = bi.biCompression;
196 if (!(nbits == 1 || nbits == 4 || nbits == 8 || nbits == 16 || nbits == 24 || nbits == 32) ||
197 bi.biPlanes != 1 || comp > BMP_BITFIELDS)
198 return false; // weird BMP image
199 if (!(comp == BMP_RGB || (nbits == 4 && comp == BMP_RLE4) ||
200 (nbits == 8 && comp == BMP_RLE8) || ((nbits == 16 || nbits == 32) && comp == BMP_BITFIELDS)))
201 return false; // weird compression type
202 if (bi.biHeight == INT_MIN)
203 return false; // out of range for positive int
204 if (bi.biWidth <= 0 || !bi.biHeight || quint64(bi.biWidth) * qAbs(bi.biHeight) > 16384 * 16384)
205 return false;
206
207 return true;
208}
209
210static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 datapos, qint64 startpos, QImage &image)
211{
212 QIODevice* d = s.device();
213 if (d->atEnd()) // end of stream/file
214 return false;
215#if 0
216 qDebug("offset...........%lld", datapos);
217 qDebug("startpos.........%lld", startpos);
218 qDebug("biSize...........%d", bi.biSize);
219 qDebug("biWidth..........%d", bi.biWidth);
220 qDebug("biHeight.........%d", bi.biHeight);
221 qDebug("biPlanes.........%d", bi.biPlanes);
222 qDebug("biBitCount.......%d", bi.biBitCount);
223 qDebug("biCompression....%d", bi.biCompression);
224 qDebug("biSizeImage......%d", bi.biSizeImage);
225 qDebug("biXPelsPerMeter..%d", bi.biXPelsPerMeter);
226 qDebug("biYPelsPerMeter..%d", bi.biYPelsPerMeter);
227 qDebug("biClrUsed........%d", bi.biClrUsed);
228 qDebug("biClrImportant...%d", bi.biClrImportant);
229#endif
230 int w = bi.biWidth, h = bi.biHeight, nbits = bi.biBitCount;
231 int t = bi.biSize, comp = bi.biCompression;
232 uint red_mask = 0;
233 uint green_mask = 0;
234 uint blue_mask = 0;
235 uint alpha_mask = 0;
236 uint red_shift = 0;
237 uint green_shift = 0;
238 uint blue_shift = 0;
239 uint alpha_shift = 0;
240 uint red_scale = 0;
241 uint green_scale = 0;
242 uint blue_scale = 0;
243 uint alpha_scale = 0;
244 bool bitfields = comp == BMP_BITFIELDS || comp == BMP_ALPHABITFIELDS;
245
246 if (!d->isSequential())
247 d->seek(startpos + bi.biSize); // goto start of colormap or masks
248
249 if (bi.biSize >= BMP_WIN4) {
250 red_mask = bi.biRedMask;
251 green_mask = bi.biGreenMask;
252 blue_mask = bi.biBlueMask;
253 alpha_mask = bi.biAlphaMask;
254 } else if (bitfields && (nbits == 16 || nbits == 32)) {
255 if (d->read((char *)&red_mask, sizeof(red_mask)) != sizeof(red_mask))
256 return false;
257 if (d->read((char *)&green_mask, sizeof(green_mask)) != sizeof(green_mask))
258 return false;
259 if (d->read((char *)&blue_mask, sizeof(blue_mask)) != sizeof(blue_mask))
260 return false;
261 if (comp == BMP_ALPHABITFIELDS && d->read((char *)&alpha_mask, sizeof(alpha_mask)) != sizeof(alpha_mask))
262 return false;
263 }
264
265 bool transp = bitfields || (comp == BMP_RGB && nbits == 32 && alpha_mask == 0xff000000);
266 transp = transp && alpha_mask;
267
268 int ncols = 0;
269 int depth = 0;
271 switch (nbits) {
272 case 32:
273 case 24:
274 case 16:
275 depth = 32;
277 break;
278 case 8:
279 case 4:
280 depth = 8;
282 break;
283 case 1:
284 depth = 1;
286 break;
287 default:
288 return false;
289 break;
290 }
291
292 if (depth != 32) {
293 ncols = bi.biClrUsed ? bi.biClrUsed : 1 << nbits;
294 if (ncols < 1 || ncols > 256) // sanity check - don't run out of mem if color table is broken
295 return false;
296 }
297
298 if (bi.biHeight < 0)
299 h = -h; // support images with negative height
300
302 return false;
303 if (ncols > 0) { // read color table
304 image.setColorCount(ncols);
305 uchar rgb[4];
306 int rgb_len = t == BMP_OLD ? 3 : 4;
307 for (int i=0; i<ncols; i++) {
308 if (d->read((char *)rgb, rgb_len) != rgb_len)
309 return false;
310 image.setColor(i, qRgb(rgb[2],rgb[1],rgb[0]));
311 if (d->atEnd()) // truncated file
312 return false;
313 }
314 } else if (bitfields && (nbits == 16 || nbits == 32)) {
315 red_shift = calc_shift(red_mask);
316 if (((red_mask >> red_shift) + 1) == 0)
317 return false;
318 red_scale = calc_scale(red_mask >> red_shift);
319 green_shift = calc_shift(green_mask);
320 if (((green_mask >> green_shift) + 1) == 0)
321 return false;
322 green_scale = calc_scale(green_mask >> green_shift);
323 blue_shift = calc_shift(blue_mask);
324 if (((blue_mask >> blue_shift) + 1) == 0)
325 return false;
326 blue_scale = calc_scale(blue_mask >> blue_shift);
327 alpha_shift = calc_shift(alpha_mask);
328 if (((alpha_mask >> alpha_shift) + 1) == 0)
329 return false;
330 alpha_scale = calc_scale(alpha_mask >> alpha_shift);
331 } else if (comp == BMP_RGB && (nbits == 24 || nbits == 32)) {
332 blue_mask = 0x000000ff;
333 green_mask = 0x0000ff00;
334 red_mask = 0x00ff0000;
335 blue_shift = 0;
336 green_shift = 8;
337 red_shift = 16;
338 blue_scale = green_scale = red_scale = 0;
339 if (transp) {
340 alpha_shift = calc_shift(alpha_mask);
341 if (((alpha_mask >> alpha_shift) + 1) == 0)
342 return false;
343 alpha_scale = calc_scale(alpha_mask >> alpha_shift);
344 }
345 } else if (comp == BMP_RGB && nbits == 16) {
346 blue_mask = 0x001f;
347 green_mask = 0x03e0;
348 red_mask = 0x7c00;
349 blue_shift = 0;
350 green_shift = 5;
351 red_shift = 10;
352 blue_scale = green_scale = red_scale = 3;
353 }
354
355 image.setDotsPerMeterX(bi.biXPelsPerMeter);
356 image.setDotsPerMeterY(bi.biYPelsPerMeter);
357
358#if 0
359 qDebug("Rmask: %08x Rshift: %08x Rscale:%08x", red_mask, red_shift, red_scale);
360 qDebug("Gmask: %08x Gshift: %08x Gscale:%08x", green_mask, green_shift, green_scale);
361 qDebug("Bmask: %08x Bshift: %08x Bscale:%08x", blue_mask, blue_shift, blue_scale);
362 qDebug("Amask: %08x Ashift: %08x Ascale:%08x", alpha_mask, alpha_shift, alpha_scale);
363#endif
364
365 if (datapos >= 0 && datapos > d->pos()) {
366 if (!d->isSequential())
367 d->seek(datapos); // start of image data
368 }
369
370 int bpl = image.bytesPerLine();
371 uchar *data = image.bits();
372
373 if (nbits == 1) { // 1 bit BMP image
374 while (--h >= 0) {
375 if (d->read((char*)(data + h*bpl), bpl) != bpl)
376 break;
377 }
378 if (ncols == 2 && qGray(image.color(0)) < qGray(image.color(1)))
379 swapPixel01(&image); // pixel 0 is white!
380 }
381
382 else if (nbits == 4) { // 4 bit BMP image
383 int buflen = ((w+7)/8)*4;
384 uchar *buf = new uchar[buflen];
385 if (comp == BMP_RLE4) { // run length compression
386 int x=0, y=0, c, i;
387 quint8 b;
388 uchar *p = data + (h-1)*bpl;
389 const uchar *endp = p + w;
390 while (y < h) {
391 if (!d->getChar((char *)&b))
392 break;
393 if (b == 0) { // escape code
394 if (!d->getChar((char *)&b) || b == 1) {
395 y = h; // exit loop
396 } else switch (b) {
397 case 0: // end of line
398 x = 0;
399 y++;
400 p = data + (h-y-1)*bpl;
401 break;
402 case 2: // delta (jump)
403 {
404 quint8 tmp;
405 d->getChar((char *)&tmp);
406 x += tmp;
407 d->getChar((char *)&tmp);
408 y += tmp;
409 }
410
411 // Protection
412 if ((uint)x >= (uint)w)
413 x = w-1;
414 if ((uint)y >= (uint)h)
415 y = h-1;
416
417 p = data + (h-y-1)*bpl + x;
418 break;
419 default: // absolute mode
420 // Protection
421 if (p + b > endp)
422 b = endp-p;
423
424 i = (c = b)/2;
425 while (i--) {
426 d->getChar((char *)&b);
427 *p++ = b >> 4;
428 *p++ = b & 0x0f;
429 }
430 if (c & 1) {
431 unsigned char tmp;
432 d->getChar((char *)&tmp);
433 *p++ = tmp >> 4;
434 }
435 if ((((c & 3) + 1) & 2) == 2)
436 d->getChar(nullptr); // align on word boundary
437 x += c;
438 }
439 } else { // encoded mode
440 // Protection
441 if (p + b > endp)
442 b = endp-p;
443
444 i = (c = b)/2;
445 d->getChar((char *)&b); // 2 pixels to be repeated
446 while (i--) {
447 *p++ = b >> 4;
448 *p++ = b & 0x0f;
449 }
450 if (c & 1)
451 *p++ = b >> 4;
452 x += c;
453 }
454 }
455 } else if (comp == BMP_RGB) { // no compression
456 memset(data, 0, h*bpl);
457 while (--h >= 0) {
458 if (d->read((char*)buf,buflen) != buflen)
459 break;
460 uchar *p = data + h*bpl;
461 uchar *b = buf;
462 for (int i=0; i<w/2; i++) { // convert nibbles to bytes
463 *p++ = *b >> 4;
464 *p++ = *b++ & 0x0f;
465 }
466 if (w & 1) // the last nibble
467 *p = *b >> 4;
468 }
469 }
470 delete [] buf;
471 }
472
473 else if (nbits == 8) { // 8 bit BMP image
474 if (comp == BMP_RLE8) { // run length compression
475 int x=0, y=0;
476 quint8 b;
477 uchar *p = data + (h-1)*bpl;
478 const uchar *endp = p + w;
479 while (y < h) {
480 if (!d->getChar((char *)&b))
481 break;
482 if (b == 0) { // escape code
483 if (!d->getChar((char *)&b) || b == 1) {
484 y = h; // exit loop
485 } else switch (b) {
486 case 0: // end of line
487 x = 0;
488 y++;
489 p = data + (h-y-1)*bpl;
490 break;
491 case 2: // delta (jump)
492 {
493 quint8 tmp;
494 d->getChar((char *)&tmp);
495 x += tmp;
496 d->getChar((char *)&tmp);
497 y += tmp;
498 }
499
500 // Protection
501 if ((uint)x >= (uint)w)
502 x = w-1;
503 if ((uint)y >= (uint)h)
504 y = h-1;
505
506 p = data + (h-y-1)*bpl + x;
507 break;
508 default: // absolute mode
509 // Protection
510 if (p + b > endp)
511 b = endp-p;
512
513 if (d->read((char *)p, b) != b)
514 return false;
515 if ((b & 1) == 1)
516 d->getChar(nullptr); // align on word boundary
517 x += b;
518 p += b;
519 }
520 } else { // encoded mode
521 // Protection
522 if (p + b > endp)
523 b = endp-p;
524
525 char tmp;
526 d->getChar(&tmp);
527 memset(p, tmp, b); // repeat pixel
528 x += b;
529 p += b;
530 }
531 }
532 } else if (comp == BMP_RGB) { // uncompressed
533 while (--h >= 0) {
534 if (d->read((char *)data + h*bpl, bpl) != bpl)
535 break;
536 }
537 }
538 }
539
540 else if (nbits == 16 || nbits == 24 || nbits == 32) { // 16,24,32 bit BMP image
541 QRgb *p;
542 QRgb *end;
543 uchar *buf24 = new uchar[bpl];
544 int bpl24 = ((w*nbits+31)/32)*4;
545 uchar *b;
546 int c;
547
548 while (--h >= 0) {
549 p = (QRgb *)(data + h*bpl);
550 end = p + w;
551 if (d->read((char *)buf24,bpl24) != bpl24)
552 break;
553 b = buf24;
554 while (p < end) {
555 c = *(uchar*)b | (*(uchar*)(b+1)<<8);
556 if (nbits > 16)
557 c |= *(uchar*)(b+2)<<16;
558 if (nbits > 24)
559 c |= *(uchar*)(b+3)<<24;
560 *p++ = qRgba(apply_scale((c & red_mask) >> red_shift, red_scale),
561 apply_scale((c & green_mask) >> green_shift, green_scale),
562 apply_scale((c & blue_mask) >> blue_shift, blue_scale),
563 transp ? apply_scale((c & alpha_mask) >> alpha_shift, alpha_scale) : 0xff);
564 b += nbits/8;
565 }
566 }
567 delete[] buf24;
568 }
569
570 if (bi.biHeight < 0) {
571 // Flip the image
572 uchar *buf = new uchar[bpl];
573 h = -bi.biHeight;
574 for (int y = 0; y < h/2; ++y) {
575 memcpy(buf, data + y*bpl, bpl);
576 memcpy(data + y*bpl, data + (h-y-1)*bpl, bpl);
577 memcpy(data + (h-y-1)*bpl, buf, bpl);
578 }
579 delete [] buf;
580 }
581
582 return true;
583}
584
585bool qt_write_dib(QDataStream &s, const QImage &image, int bpl, int bpl_bmp, int nbits)
586{
587 QIODevice* d = s.device();
588 if (!d->isWritable())
589 return false;
590
591 BMP_INFOHDR bi;
592 bi.biSize = BMP_WIN; // build info header
593 bi.biWidth = image.width();
594 bi.biHeight = image.height();
595 bi.biPlanes = 1;
596 bi.biBitCount = nbits;
598 bi.biSizeImage = bpl_bmp*image.height();
599 bi.biXPelsPerMeter = image.dotsPerMeterX() ? image.dotsPerMeterX()
600 : 2834; // 72 dpi default
601 bi.biYPelsPerMeter = image.dotsPerMeterY() ? image.dotsPerMeterY() : 2834;
602 bi.biClrUsed = image.colorCount();
603 bi.biClrImportant = image.colorCount();
604 s << bi; // write info header
605 if (s.status() != QDataStream::Ok)
606 return false;
607
608 if (image.depth() != 32) { // write color table
609 uchar *color_table = new uchar[4*image.colorCount()];
610 uchar *rgb = color_table;
611 QList<QRgb> c = image.colorTable();
612 for (int i = 0; i < image.colorCount(); i++) {
613 *rgb++ = qBlue (c[i]);
614 *rgb++ = qGreen(c[i]);
615 *rgb++ = qRed (c[i]);
616 *rgb++ = 0;
617 }
618 if (d->write((char *)color_table, 4*image.colorCount()) == -1) {
619 delete [] color_table;
620 return false;
621 }
622 delete [] color_table;
623 }
624
625 int y;
626
627 if (nbits == 1 || nbits == 8) { // direct output
628 for (y=image.height()-1; y>=0; y--) {
629 if (d->write((const char*)image.constScanLine(y), bpl) == -1)
630 return false;
631 }
632 return true;
633 }
634
635 uchar *buf = new uchar[bpl_bmp];
636 uchar *b, *end;
637 const uchar *p;
638
639 memset(buf, 0, bpl_bmp);
640 for (y=image.height()-1; y>=0; y--) { // write the image bits
641 if (nbits == 4) { // convert 8 -> 4 bits
642 p = image.constScanLine(y);
643 b = buf;
644 end = b + image.width()/2;
645 while (b < end) {
646 *b++ = (*p << 4) | (*(p+1) & 0x0f);
647 p += 2;
648 }
649 if (image.width() & 1)
650 *b = *p << 4;
651 } else { // 32 bits
652 const QRgb *p = (const QRgb *)image.constScanLine(y);
653 const QRgb *end = p + image.width();
654 b = buf;
655 while (p < end) {
656 *b++ = qBlue(*p);
657 *b++ = qGreen(*p);
658 *b++ = qRed(*p);
659 p++;
660 }
661 }
662 if (bpl_bmp != d->write((char*)buf, bpl_bmp)) {
663 delete[] buf;
664 return false;
665 }
666 }
667 delete[] buf;
668 return true;
669}
670
672 m_format(fmt), state(Ready)
673{
674}
675
676QByteArray QBmpHandler::formatName() const
677{
678 return m_format == BmpFormat ? "bmp" : "dib";
679}
680
681bool QBmpHandler::readHeader()
682{
683 state = Error;
684
685 QIODevice *d = device();
686 QDataStream s(d);
687 startpos = d->pos();
688
689 // Intel byte order
690 s.setByteOrder(QDataStream::LittleEndian);
691
692 // read BMP file header
693 if (m_format == BmpFormat && !read_dib_fileheader(s, fileHeader))
694 return false;
695
696 // read BMP info header
697 if (!read_dib_infoheader(s, infoHeader))
698 return false;
699
700 state = ReadHeader;
701 return true;
702}
703
705{
706 if (m_format == BmpFormat && state == Ready && !canRead(device()))
707 return false;
708
709 if (state != Error) {
710 setFormat(formatName());
711 return true;
712 }
713
714 return false;
715}
716
718{
719 if (!device) {
720 qWarning("QBmpHandler::canRead() called with 0 pointer");
721 return false;
722 }
723
724 char head[2];
725 if (device->peek(head, sizeof(head)) != sizeof(head))
726 return false;
727
728 return (qstrncmp(head, "BM", 2) == 0);
729}
730
732{
733 if (state == Error)
734 return false;
735
736 if (!image) {
737 qWarning("QBmpHandler::read: cannot read into null pointer");
738 return false;
739 }
740
741 if (state == Ready && !readHeader()) {
742 state = Error;
743 return false;
744 }
745
746 QIODevice *d = device();
747 QDataStream s(d);
748
749 // Intel byte order
750 s.setByteOrder(QDataStream::LittleEndian);
751
752 // read image
753 qint64 datapos = startpos;
754 if (m_format == BmpFormat) {
755 datapos += fileHeader.bfOffBits;
756 } else {
757 // QTBUG-100351: We have no file header when reading dib format so we have to depend on the size of the
758 // buffer and the biSizeImage value to find where the pixel data starts since there's sometimes optional
759 // color mask values after biSize, like for example when pasting from the windows snipping tool.
760 if (infoHeader.biSizeImage > 0 && infoHeader.biSizeImage < d->size()) {
761 datapos = d->size() - infoHeader.biSizeImage;
762 } else {
763 // And sometimes biSizeImage is not filled in like when pasting from Microsoft Edge, so then we just
764 // have to assume the optional color mask values are there.
765 datapos += infoHeader.biSize;
766
767 if (infoHeader.biBitCount == 16 || infoHeader.biBitCount == 32) {
768 if (infoHeader.biCompression == BMP_BITFIELDS) {
769 datapos += 12;
770 } else if (infoHeader.biCompression == BMP_ALPHABITFIELDS) {
771 datapos += 16;
772 }
773 }
774 }
775 }
776 const bool readSuccess = m_format == BmpFormat ?
777 read_dib_body(s, infoHeader, datapos, startpos + BMP_FILEHDR_SIZE, *image) :
778 read_dib_body(s, infoHeader, datapos, startpos, *image);
779 if (!readSuccess)
780 return false;
781
782 state = Ready;
783 return true;
784}
785
787{
789 switch (img.format()) {
794 image = img;
795 break;
797 image = img.convertToFormat(QImage::Format_Mono);
798 break;
801 image = img.convertToFormat(QImage::Format_Indexed8);
802 break;
803 default:
804 if (img.hasAlphaChannel())
805 image = img.convertToFormat(QImage::Format_ARGB32);
806 else
807 image = img.convertToFormat(QImage::Format_RGB32);
808 break;
809 }
810
811 int nbits;
812 qsizetype bpl_bmp;
813 // Calculate a minimum bytes-per-line instead of using whatever value this QImage is using internally.
814 qsizetype bpl = ((image.width() * image.depth() + 31) >> 5) << 2;
815
816 if (image.depth() == 8 && image.colorCount() <= 16) {
817 bpl_bmp = (((bpl+1)/2+3)/4)*4;
818 nbits = 4;
819 } else if (image.depth() == 32) {
820 bpl_bmp = ((image.width()*24+31)/32)*4;
821 nbits = 24;
822 } else {
823 bpl_bmp = bpl;
824 nbits = image.depth();
825 }
826 if (qsizetype(int(bpl_bmp)) != bpl_bmp)
827 return false;
828
829 if (m_format == DibFormat) {
830 QDataStream dibStream(device());
831 dibStream.setByteOrder(QDataStream::LittleEndian); // Intel byte order
832 return qt_write_dib(dibStream, img, bpl, bpl_bmp, nbits);
833 }
834
835 QIODevice *d = device();
836 QDataStream s(d);
837 BMP_FILEHDR bf;
838
839 // Intel byte order
840 s.setByteOrder(QDataStream::LittleEndian);
841
842 // build file header
843 memcpy(bf.bfType, "BM", 2);
844
845 // write file header
846 bf.bfReserved1 = 0;
847 bf.bfReserved2 = 0;
848 bf.bfOffBits = BMP_FILEHDR_SIZE + BMP_WIN + image.colorCount() * 4;
849 bf.bfSize = bf.bfOffBits + bpl_bmp*image.height();
850 if (qsizetype(bf.bfSize) != bf.bfOffBits + bpl_bmp*image.height())
851 return false;
852 s << bf;
853
854 // write image
855 return qt_write_dib(s, image, bpl, bpl_bmp, nbits);
856}
857
859{
860 return option == Size
861 || option == ImageFormat;
862}
863
865{
866 if (option == Size) {
867 if (state == Error)
868 return QVariant();
869 if (state == Ready && !const_cast<QBmpHandler*>(this)->readHeader())
870 return QVariant();
871 return QSize(infoHeader.biWidth, infoHeader.biHeight);
872 } else if (option == ImageFormat) {
873 if (state == Error)
874 return QVariant();
875 if (state == Ready && !const_cast<QBmpHandler*>(this)->readHeader())
876 return QVariant();
878 switch (infoHeader.biBitCount) {
879 case 32:
880 case 24:
881 case 16:
882 if ((infoHeader.biCompression == BMP_BITFIELDS || infoHeader.biCompression == BMP_ALPHABITFIELDS) && infoHeader.biSize >= BMP_WIN4 && infoHeader.biAlphaMask)
884 else
886 break;
887 case 8:
888 case 4:
890 break;
891 default:
893 }
894 return format;
895 }
896 return QVariant();
897}
898
900{
903}
904
906
907#endif // QT_NO_IMAGEFORMAT_BMP
IOBluetoothDevice * device
bool supportsOption(ImageOption option) const override
Returns true if the QImageIOHandler supports the option option; otherwise returns false.
QVariant option(ImageOption option) const override
Returns the value assigned to option as a QVariant.
QBmpHandler(InternalFormat fmt=BmpFormat)
bool canRead() const override
Returns true if an image can be read from the device (i.e., the image format is supported,...
bool write(const QImage &image) override
Writes the image image to the assigned device.
void setOption(ImageOption option, const QVariant &value) override
Sets the option option with the value value.
bool read(QImage *image) override
Read an image from the device, and stores it in image.
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore\reentrant
Definition qdatastream.h:30
int readRawData(char *, int len)
Reads at most len bytes from the stream into s and returns the number of bytes read.
int writeRawData(const char *, int len)
Writes len bytes from s to the stream.
void setByteOrder(ByteOrder)
Sets the serialization byte order to bo.
\inmodule QtCore \reentrant
Definition qiodevice.h:34
qint64 peek(char *data, qint64 maxlen)
ImageOption
This enum describes the different options supported by QImageIOHandler.
static bool allocateImage(QSize size, QImage::Format format, QImage *image)
QByteArray format() const
Returns the format that is currently assigned to QImageIOHandler.
QIODevice * device() const
Returns the device currently assigned to the QImageIOHandler.
void setFormat(const QByteArray &format)
Sets the format of the QImageIOHandler to format.
\inmodule QtGui
Definition qimage.h:37
Format
The following image formats are available in Qt.
Definition qimage.h:41
@ Format_Alpha8
Definition qimage.h:65
@ Format_RGB32
Definition qimage.h:46
@ Format_MonoLSB
Definition qimage.h:44
@ Format_Mono
Definition qimage.h:43
@ Format_Indexed8
Definition qimage.h:45
@ Format_ARGB32
Definition qimage.h:47
@ Format_Grayscale8
Definition qimage.h:66
Definition qlist.h:74
\inmodule QtCore
Definition qsize.h:25
\inmodule QtCore
Definition qvariant.h:64
QPixmap p2
else opt state
[0]
Combined button and popup list for selecting options.
Definition image.cpp:4
const int BMP_BITFIELDS
static uint calc_shift(uint mask)
static uint calc_scale(uint low_mask)
static bool read_dib_infoheader(QDataStream &s, BMP_INFOHDR &bi)
static QDataStream & operator>>(QDataStream &s, BMP_FILEHDR &bf)
static QDataStream & operator<<(QDataStream &s, const BMP_FILEHDR &bf)
bool qt_write_dib(QDataStream &s, const QImage &image, int bpl, int bpl_bmp, int nbits)
const int BMP_RLE4
const int BMP_ALPHABITFIELDS
static uint apply_scale(uint value, uint scale)
const int BMP_OLD
const int BMP_FILEHDR_SIZE
static QT_BEGIN_NAMESPACE void swapPixel01(QImage *image)
const int BMP_WIN
static bool read_dib_fileheader(QDataStream &s, BMP_FILEHDR &bf)
const int BMP_WIN4
static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 datapos, qint64 startpos, QImage &image)
const int BMP_RLE8
const int BMP_OS2
const int BMP_RGB
const int BMP_WIN5
int qstrncmp(const char *str1, const char *str2, size_t len)
#define rgb(r, g, b)
Definition qcolor.cpp:124
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qDebug
[1]
Definition qlogging.h:160
#define qWarning
Definition qlogging.h:162
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
GLboolean GLboolean GLboolean b
GLint GLint GLint GLint GLint x
[0]
GLint GLenum GLsizei GLsizei GLsizei depth
GLfloat GLfloat GLfloat w
[0]
GLuint GLuint end
GLenum GLuint GLenum GLsizei const GLchar * buf
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
GLint GLsizei GLsizei GLenum format
GLint y
GLfloat GLfloat GLfloat GLfloat h
const GLubyte * c
GLint void * img
Definition qopenglext.h:233
GLdouble GLdouble t
Definition qopenglext.h:243
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
GLfloat GLfloat p
[1]
GLuint GLenum option
GLenum GLenum GLenum GLenum GLenum scale
QT_BEGIN_NAMESPACE typedef unsigned int QRgb
Definition qrgb.h:13
constexpr QRgb qRgb(int r, int g, int b)
Definition qrgb.h:30
constexpr int qRed(QRgb rgb)
Definition qrgb.h:18
constexpr int qGreen(QRgb rgb)
Definition qrgb.h:21
constexpr int qGray(int r, int g, int b)
Definition qrgb.h:36
constexpr QRgb qRgba(int r, int g, int b, int a)
Definition qrgb.h:33
constexpr int qBlue(QRgb rgb)
Definition qrgb.h:24
#define Q_UNUSED(x)
unsigned char uchar
Definition qtypes.h:27
short qint16
Definition qtypes.h:42
unsigned long long quint64
Definition qtypes.h:56
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned int uint
Definition qtypes.h:29
long long qint64
Definition qtypes.h:55
unsigned char quint8
Definition qtypes.h:41
QVideoFrameFormat::PixelFormat fmt
qint16 bfReserved1
qint32 bfOffBits
qint16 bfReserved2
char bfType[2]
qint16 biPlanes
qint32 biSizeImage
qint32 biClrImportant
qint32 biCompression
qint32 biGammaGreen
qint32 biGammaRed
qint16 biBitCount
qint32 biCSType
qint32 biReserved
quint32 biGreenMask
qint32 biIntent
qint32 biClrUsed
qint32 biYPelsPerMeter
qint32 biXPelsPerMeter
qint32 biHeight
qint32 biGammaBlue
quint32 biAlphaMask
quint32 biRedMask
qint32 biProfileData
qint32 biProfileSize
quint32 biBlueMask
qint32 biEndpoints[9]