Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qicohandler.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 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// Qt-Security score:critical reason:data-parser
4
5/*!
6 \class QtIcoHandler
7 \since 4.4
8 \brief The QtIcoHandler class provides support for the ICO image format.
9 \internal
10*/
11
12
13
14#include "qicohandler.h"
15#include <QtCore/qendian.h>
16#include <private/qendian_p.h>
17#include <QtGui/QImage>
18#include <QtCore/QBuffer>
19#include <QtCore/QFile>
20#include <QtCore/QLoggingCategory>
21#include <qvariant.h>
22
24
25Q_LOGGING_CATEGORY(lcIco, "qt.gui.imageio.ico")
26
27namespace {
28
29// These next two structs represent how the icon information is stored
30// in an ICO file.
31typedef struct
32{
33 quint8 bWidth; // Width of the image
34 quint8 bHeight; // Height of the image (actual height, not times 2)
35 quint8 bColorCount; // Number of colors in image (0 if >=8bpp) [ not ture ]
36 quint8 bReserved; // Reserved
37 quint16_le wPlanes; // Color Planes
38 quint16_le wBitCount; // Bits per pixel
39 quint32_le dwBytesInRes; // how many bytes in this resource?
40 quint32_le dwImageOffset; // where in the file is this image
41} ICONDIRENTRY, *LPICONDIRENTRY;
42#define ICONDIRENTRY_SIZE 16
43
44typedef struct
45{
47 quint16_le idType; // resource type (1 for icons, 2 for cursors)
48 quint16_le idCount; // how many images?
49 ICONDIRENTRY idEntries[1]; // the entries for each image
50} ICONDIR, *LPICONDIR;
51#define ICONDIR_SIZE 6 // Exclude the idEntries field
52
53typedef struct { // BMP information header
54 quint32_le biSize; // size of this struct
55 quint32_le biWidth; // pixmap width
56 quint32_le biHeight; // pixmap height (specifies the combined height of the XOR and AND masks)
57 quint16_le biPlanes; // should be 1
58 quint16_le biBitCount; // number of bits per pixel
59 quint32_le biCompression; // compression method
60 quint32_le biSizeImage; // size of image
61 quint32_le biXPelsPerMeter; // horizontal resolution
62 quint32_le biYPelsPerMeter; // vertical resolution
63 quint32_le biClrUsed; // number of colors used
64 quint32_le biClrImportant; // number of important colors
65} BMP_INFOHDR ,*LPBMP_INFOHDR;
66#define BMP_INFOHDR_SIZE 40
67
68}
69
71{
72public:
73 ICOReader(QIODevice * iodevice);
74 int count();
75 QImage iconAt(int index);
76 static bool canRead(QIODevice *iodev);
77
78 static QList<QImage> read(QIODevice *device);
79
80 static bool write(QIODevice *device, const QList<QImage> &images);
81
82 bool readIconEntry(int index, ICONDIRENTRY * iconEntry);
83
84private:
85 bool readHeader();
86
87 bool readBMPHeader(quint32 imageOffset, BMP_INFOHDR * header);
88 void findColorInfo(QImage & image);
89 void readColorTable(QImage & image);
90
91 void readBMP(QImage & image);
92 void read1BitBMP(QImage & image);
93 void read4BitBMP(QImage & image);
94 void read8BitBMP(QImage & image);
95 void read16_24_32BMP(QImage & image);
96
97 struct IcoAttrib
98 {
99 int nbits;
100 int ncolors;
101 int h;
102 int w;
103 int depth;
104 } icoAttrib;
105
106 QIODevice * iod;
107 qint64 startpos;
108 bool headerRead;
109 ICONDIR iconDir;
110
111};
112
113// Data readers and writers that takes care of alignment and endian stuff.
114static bool readIconDirEntry(QIODevice *iodev, ICONDIRENTRY *iconDirEntry)
115{
116 if (iodev)
117 return (iodev->read((char*)iconDirEntry, ICONDIRENTRY_SIZE) == ICONDIRENTRY_SIZE);
118 return false;
119}
120
121static bool writeIconDirEntry(QIODevice *iodev, const ICONDIRENTRY &iconEntry)
122{
123 if (iodev)
124 return iodev->write((char*)&iconEntry, ICONDIRENTRY_SIZE) == ICONDIRENTRY_SIZE;
125 return false;
126}
127
128static bool readIconDir(QIODevice *iodev, ICONDIR *iconDir)
129{
130 if (iodev)
131 return (iodev->read((char*)iconDir, ICONDIR_SIZE) == ICONDIR_SIZE);
132 return false;
133}
134
135static bool writeIconDir(QIODevice *iodev, const ICONDIR &iconDir)
136{
137 if (iodev)
138 return iodev->write((char*)&iconDir, 6) == 6;
139 return false;
140}
141
142static bool readBMPInfoHeader(QIODevice *iodev, BMP_INFOHDR *pHeader)
143{
144 if (iodev)
145 return (iodev->read((char*)pHeader, BMP_INFOHDR_SIZE) == BMP_INFOHDR_SIZE);
146 return false;
147}
148
149static bool writeBMPInfoHeader(QIODevice *iodev, const BMP_INFOHDR &header)
150{
151 if (iodev)
152 return iodev->write((char*)&header, BMP_INFOHDR_SIZE) == BMP_INFOHDR_SIZE;
153 return false;
154}
155
156
157ICOReader::ICOReader(QIODevice * iodevice)
158: iod(iodevice)
159, startpos(0)
160, headerRead(false)
161{
162}
163
164
166{
167 if (readHeader())
168 return iconDir.idCount;
169 return 0;
170}
171
172bool ICOReader::canRead(QIODevice *iodev)
173{
174 bool isProbablyICO = false;
175 if (iodev) {
176 qint64 oldPos = iodev->pos();
177
178 ICONDIR ikonDir;
179 if (readIconDir(iodev, &ikonDir)) {
180 if (readIconDirEntry(iodev, &ikonDir.idEntries[0])) {
181 // ICO format does not have a magic identifier, so we read 6 different values, which will hopefully be enough to identify the file.
182 if ( ikonDir.idReserved == 0
183 && (ikonDir.idType == 1 || ikonDir.idType == 2)
184 && ikonDir.idEntries[0].bReserved == 0
185 && (ikonDir.idEntries[0].wPlanes <= 1 || ikonDir.idType == 2)
186 && (ikonDir.idEntries[0].wBitCount <= 32 || ikonDir.idType == 2) // Bits per pixel
187 && ikonDir.idEntries[0].dwBytesInRes >= 40 // Must be over 40, since sizeof (infoheader) == 40
188 ) {
189 isProbablyICO = true;
190 }
191
192 if (iodev->isSequential()) {
193 // Our structs might be padded due to alignment, so we need to fetch each member before we ungetChar() !
194 quint32 tmp = ikonDir.idEntries[0].dwImageOffset;
195 iodev->ungetChar((tmp >> 24) & 0xff);
196 iodev->ungetChar((tmp >> 16) & 0xff);
197 iodev->ungetChar((tmp >> 8) & 0xff);
198 iodev->ungetChar(tmp & 0xff);
199
200 tmp = ikonDir.idEntries[0].dwBytesInRes;
201 iodev->ungetChar((tmp >> 24) & 0xff);
202 iodev->ungetChar((tmp >> 16) & 0xff);
203 iodev->ungetChar((tmp >> 8) & 0xff);
204 iodev->ungetChar(tmp & 0xff);
205
206 tmp = ikonDir.idEntries[0].wBitCount;
207 iodev->ungetChar((tmp >> 8) & 0xff);
208 iodev->ungetChar(tmp & 0xff);
209
210 tmp = ikonDir.idEntries[0].wPlanes;
211 iodev->ungetChar((tmp >> 8) & 0xff);
212 iodev->ungetChar(tmp & 0xff);
213
214 iodev->ungetChar(ikonDir.idEntries[0].bReserved);
215 iodev->ungetChar(ikonDir.idEntries[0].bColorCount);
216 iodev->ungetChar(ikonDir.idEntries[0].bHeight);
217 iodev->ungetChar(ikonDir.idEntries[0].bWidth);
218 }
219 }
220
221 if (iodev->isSequential()) {
222 // Our structs might be padded due to alignment, so we need to fetch each member before we ungetChar() !
223 quint32 tmp = ikonDir.idCount;
224 iodev->ungetChar((tmp >> 8) & 0xff);
225 iodev->ungetChar(tmp & 0xff);
226
227 tmp = ikonDir.idType;
228 iodev->ungetChar((tmp >> 8) & 0xff);
229 iodev->ungetChar(tmp & 0xff);
230
231 tmp = ikonDir.idReserved;
232 iodev->ungetChar((tmp >> 8) & 0xff);
233 iodev->ungetChar(tmp & 0xff);
234 }
235 }
236 if (!iodev->isSequential()) iodev->seek(oldPos);
237 }
238
239 return isProbablyICO;
240}
241
242bool ICOReader::readHeader()
243{
244 if (iod && !headerRead) {
245 startpos = iod->pos();
246 if (readIconDir(iod, &iconDir)) {
247 if (iconDir.idReserved == 0 && (iconDir.idType == 1 || iconDir.idType == 2))
248 headerRead = true;
249 }
250 }
251
252 return headerRead;
253}
254
255bool ICOReader::readIconEntry(int index, ICONDIRENTRY *iconEntry)
256{
257 if (readHeader()) {
258 if (iod->seek(startpos + ICONDIR_SIZE + (index * ICONDIRENTRY_SIZE))) {
259 return readIconDirEntry(iod, iconEntry);
260 }
261 }
262 return false;
263}
264
265
266
267bool ICOReader::readBMPHeader(quint32 imageOffset, BMP_INFOHDR * header)
268{
269 if (iod) {
270 if (iod->seek(startpos + imageOffset)) {
271 if (readBMPInfoHeader(iod, header)) {
272 return true;
273 }
274 }
275 }
276 return false;
277}
278
279void ICOReader::findColorInfo(QImage & image)
280{
281 if (icoAttrib.ncolors > 0) { // set color table
282 readColorTable(image);
283 } else if (icoAttrib.nbits == 16) { // don't support RGB values for 15/16 bpp
284 image = QImage();
285 }
286}
287
288void ICOReader::readColorTable(QImage & image)
289{
290 if (iod) {
291 image.setColorCount(icoAttrib.ncolors);
292 uchar rgb[4];
293 for (int i=0; i<icoAttrib.ncolors; i++) {
294 if (iod->read((char*)rgb, 4) != 4) {
295 image = QImage();
296 break;
297 }
298 image.setColor(i, qRgb(rgb[2],rgb[1],rgb[0]));
299 }
300 } else {
301 image = QImage();
302 }
303}
304
305void ICOReader::readBMP(QImage & image)
306{
307 if (icoAttrib.nbits == 1) { // 1 bit BMP image
308 read1BitBMP(image);
309 } else if (icoAttrib.nbits == 4) { // 4 bit BMP image
310 read4BitBMP(image);
311 } else if (icoAttrib.nbits == 8) {
312 read8BitBMP(image);
313 } else if (icoAttrib.nbits == 16 || icoAttrib.nbits == 24 || icoAttrib.nbits == 32 ) { // 16,24,32 bit BMP image
314 read16_24_32BMP(image);
315 }
316}
317
318
319/**
320 * NOTE: A 1 bit BMP is only flipped vertically, and not horizontally like all other color depths!
321 * (This is the same with the bitmask)
322 *
323 */
324void ICOReader::read1BitBMP(QImage & image)
325{
326 if (iod) {
327
328 int h = image.height();
329 qsizetype bpl = image.bytesPerLine();
330
331 while (--h >= 0) {
332 if (iod->read((char*)image.scanLine(h),bpl) != bpl) {
333 image = QImage();
334 break;
335 }
336 }
337 } else {
338 image = QImage();
339 }
340}
341
342void ICOReader::read4BitBMP(QImage & image)
343{
344 if (iod) {
345
346 int h = icoAttrib.h;
347 int buflen = ((icoAttrib.w+7)/8)*4;
348 uchar *buf = new uchar[buflen];
349 Q_CHECK_PTR(buf);
350
351 while (--h >= 0) {
352 if (iod->read((char*)buf,buflen) != buflen) {
353 image = QImage();
354 break;
355 }
356 uchar *p = image.scanLine(h);
357 uchar *b = buf;
358 for (int i=0; i<icoAttrib.w/2; i++) { // convert nibbles to bytes
359 *p++ = *b >> 4;
360 *p++ = *b++ & 0x0f;
361 }
362 if (icoAttrib.w & 1) // the last nibble
363 *p = *b >> 4;
364 }
365
366 delete [] buf;
367
368 } else {
369 image = QImage();
370 }
371}
372
373void ICOReader::read8BitBMP(QImage & image)
374{
375 if (iod) {
376
377 int h = icoAttrib.h;
378 qsizetype bpl = image.bytesPerLine();
379
380 while (--h >= 0) {
381 if (iod->read((char *)image.scanLine(h), bpl) != bpl) {
382 image = QImage();
383 break;
384 }
385 }
386 } else {
387 image = QImage();
388 }
389}
390
391void ICOReader::read16_24_32BMP(QImage & image)
392{
393 if (iod) {
394 int h = icoAttrib.h;
395 QRgb *p;
396 QRgb *end;
397 uchar *buf = new uchar[image.bytesPerLine()];
398 qsizetype bpl = ((qsizetype(icoAttrib.w)*icoAttrib.nbits+31)/32)*4;
399 uchar *b;
400
401 while (--h >= 0) {
402 p = (QRgb *)image.scanLine(h);
403 end = p + icoAttrib.w;
404 if (iod->read((char *)buf, bpl) != bpl) {
405 image = QImage();
406 break;
407 }
408 b = buf;
409 while (p < end) {
410 if (icoAttrib.nbits == 24)
411 *p++ = qRgb(*(b+2), *(b+1), *b);
412 else if (icoAttrib.nbits == 32)
413 *p++ = qRgba(*(b+2), *(b+1), *b, *(b+3));
414 b += icoAttrib.nbits/8;
415 }
416 }
417
418 delete[] buf;
419
420 } else {
421 image = QImage();
422 }
423}
424
425static const char icoOrigDepthKey[] = "_q_icoOrigDepth";
426
428{
429 QImage img;
430
431 if (count() > index) { // forces header to be read
432
433 ICONDIRENTRY iconEntry;
434 if (readIconEntry(index, &iconEntry)) {
435
436 static const uchar pngMagicData[] = { 137, 80, 78, 71, 13, 10, 26, 10 };
437
438 if (!iod->seek(iconEntry.dwImageOffset)
439 || iconEntry.dwBytesInRes > iod->bytesAvailable())
440 return img;
441
442 const QByteArray pngMagic = QByteArray::fromRawData((const char*)pngMagicData, sizeof(pngMagicData));
443 const bool isPngImage = (iod->read(pngMagic.size()) == pngMagic);
444
445 if (isPngImage) {
446 iod->seek(iconEntry.dwImageOffset);
447 QImage image = QImage::fromData(iod->read(iconEntry.dwBytesInRes), "png");
448 image.setText(QLatin1String(icoOrigDepthKey), QString::number(iconEntry.wBitCount));
449 return image;
450 }
451
452 BMP_INFOHDR header;
453 if (readBMPHeader(iconEntry.dwImageOffset, &header)) {
454 icoAttrib.nbits = header.biBitCount ? header.biBitCount : iconEntry.wBitCount;
455
456 switch (icoAttrib.nbits) {
457 case 32:
458 case 24:
459 case 16:
460 icoAttrib.depth = 32;
461 break;
462 case 8:
463 case 4:
464 icoAttrib.depth = 8;
465 break;
466 case 1:
467 icoAttrib.depth = 1;
468 break;
469 default:
470 return img;
471 break;
472 }
473 if (icoAttrib.depth == 32) // there's no colormap
474 icoAttrib.ncolors = 0;
475 else // # colors used
476 icoAttrib.ncolors = header.biClrUsed ? uint(header.biClrUsed) : 1 << icoAttrib.nbits;
477 if (icoAttrib.ncolors > 256) //color table can't be more than 256
478 return img;
479 icoAttrib.w = iconEntry.bWidth;
480 if (icoAttrib.w == 0) // means 256 pixels
481 icoAttrib.w = header.biWidth;
482 icoAttrib.h = iconEntry.bHeight;
483 if (icoAttrib.h == 0) // means 256 pixels
484 icoAttrib.h = header.biHeight/2;
485 if (icoAttrib.w > 256 || icoAttrib.h > 256) // Max ico size
486 return img;
487
488 QImage::Format format = QImage::Format_ARGB32;
489 if (icoAttrib.nbits == 24)
490 format = QImage::Format_RGB32;
491 else if (icoAttrib.ncolors == 2 && icoAttrib.depth == 1)
492 format = QImage::Format_Mono;
493 else if (icoAttrib.ncolors > 0)
494 format = QImage::Format_Indexed8;
495
496 QImage image;
497 const QSize size(icoAttrib.w, icoAttrib.h);
498 if (QImageIOHandler::allocateImage(size, format, &image)) {
499 findColorInfo(image);
500 if (!image.isNull()) {
501 readBMP(image);
502 if (!image.isNull()) {
503 if (icoAttrib.nbits == 32) {
504 img = std::move(image).convertToFormat(QImage::Format_ARGB32_Premultiplied);
505 } else {
506 QImage mask(image.width(), image.height(), QImage::Format_Mono);
507 if (!mask.isNull()) {
508 mask.setColorCount(2);
509 mask.setColor(0, qRgba(255,255,255,0xff));
510 mask.setColor(1, qRgba(0 ,0 ,0 ,0xff));
511 read1BitBMP(mask);
512 if (!mask.isNull()) {
513 img = image;
514 img.setAlphaChannel(mask);
515 }
516 }
517 }
518 }
519 }
520 }
521 img.setText(QLatin1String(icoOrigDepthKey), QString::number(icoAttrib.nbits));
522 }
523 }
524 }
525
526 return img;
527}
528
529
530/*!
531 Reads all the icons from the given \a device, and returns them as
532 a list of QImage objects.
533
534 Each image has an alpha channel that represents the mask from the
535 corresponding icon.
536
537 \sa write()
538*/
539QList<QImage> ICOReader::read(QIODevice *device)
540{
541 QList<QImage> images;
542
543 ICOReader reader(device);
544 const int N = reader.count();
545 images.reserve(N);
546 for (int i = 0; i < N; i++)
547 images += reader.iconAt(i);
548
549 return images;
550}
551
552
553/*!
554 Writes all the QImages in the \a images list to the given \a
555 device. Returns \c true if the images are written successfully;
556 otherwise returns \c false.
557
558 The first image in the list is stored as the first icon in the
559 device, and is therefore used as the default icon by applications.
560 The alpha channel of each image is converted to a mask for each
561 corresponding icon.
562
563 \sa read()
564*/
565bool ICOReader::write(QIODevice *device, const QList<QImage> &images)
566{
567 bool retValue = false;
568
569 if (images.size()) {
570
571 qint64 origOffset = device->pos();
572
573 ICONDIR id;
574 id.idReserved = 0;
575 id.idType = 1;
576 id.idCount = images.size();
577
578 ICONDIRENTRY * entries = new ICONDIRENTRY[id.idCount];
579 BMP_INFOHDR * bmpHeaders = new BMP_INFOHDR[id.idCount];
580 QByteArray * imageData = new QByteArray[id.idCount];
581
582 for (int i=0; i<id.idCount; i++) {
583
584 QImage image = images[i];
585 // Scale down the image if it is larger than 256 pixels in either width or height
586 // because this is a maximum size of image in the ICO file.
587 if (image.width() > 256 || image.height() > 256)
588 {
589 image = image.scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation);
590 }
591 QImage maskImage(image.width(), image.height(), QImage::Format_Mono);
592 image = image.convertToFormat(QImage::Format_ARGB32);
593 maskImage.fill(Qt::color1);
594
595 int nbits = 32;
596 int bpl_bmp = ((image.width()*nbits+31)/32)*4;
597
598 entries[i].bColorCount = 0;
599 entries[i].bReserved = 0;
600 entries[i].wBitCount = nbits;
601 entries[i].bHeight = image.height() < 256 ? image.height() : 0; // 0 means 256
602 entries[i].bWidth = image.width() < 256 ? image.width() : 0; // 0 means 256
603 entries[i].dwBytesInRes = BMP_INFOHDR_SIZE + (bpl_bmp * image.height())
604 + (maskImage.bytesPerLine() * maskImage.height());
605 entries[i].wPlanes = 1;
606 if (i == 0)
607 entries[i].dwImageOffset = origOffset + ICONDIR_SIZE
608 + (id.idCount * ICONDIRENTRY_SIZE);
609 else
610 entries[i].dwImageOffset = entries[i-1].dwImageOffset + entries[i-1].dwBytesInRes;
611
612 bmpHeaders[i].biBitCount = entries[i].wBitCount;
613 bmpHeaders[i].biClrImportant = 0;
614 bmpHeaders[i].biClrUsed = entries[i].bColorCount;
615 bmpHeaders[i].biCompression = 0;
616 bmpHeaders[i].biHeight = entries[i].bHeight ? entries[i].bHeight * 2 : 256 * 2; // 2 is for the mask
617 bmpHeaders[i].biPlanes = entries[i].wPlanes;
618 bmpHeaders[i].biSize = BMP_INFOHDR_SIZE;
619 bmpHeaders[i].biSizeImage = entries[i].dwBytesInRes - BMP_INFOHDR_SIZE;
620 bmpHeaders[i].biWidth = entries[i].bWidth ? entries[i].bWidth : 256;
621 bmpHeaders[i].biXPelsPerMeter = 0;
622 bmpHeaders[i].biYPelsPerMeter = 0;
623
624 QBuffer buffer(&imageData[i]);
625 buffer.open(QIODevice::WriteOnly);
626
627 uchar *buf = new uchar[bpl_bmp];
628 uchar *b;
629 memset( buf, 0, bpl_bmp );
630 int y;
631 for (y = image.height() - 1; y >= 0; y--) { // write the image bits
632 // 32 bits
633 QRgb *p = (QRgb *)image.scanLine(y);
634 QRgb *end = p + image.width();
635 b = buf;
636 int x = 0;
637 while (p < end) {
638 *b++ = qBlue(*p);
639 *b++ = qGreen(*p);
640 *b++ = qRed(*p);
641 *b++ = qAlpha(*p);
642 if (qAlpha(*p) > 0) // Even mostly transparent pixels must not be masked away
643 maskImage.setPixel(x, y, 0);
644 p++;
645 x++;
646 }
647 buffer.write((char*)buf, bpl_bmp);
648 }
649 delete[] buf;
650
651 // NOTE! !! The mask is only flipped vertically - not horizontally !!
652 for (y = maskImage.height() - 1; y >= 0; y--)
653 buffer.write((char*)maskImage.scanLine(y), maskImage.bytesPerLine());
654 }
655
656 if (writeIconDir(device, id)) {
657 int i;
658 bool bOK = true;
659 for (i = 0; i < id.idCount && bOK; i++) {
660 bOK = writeIconDirEntry(device, entries[i]);
661 }
662 if (bOK) {
663 for (i = 0; i < id.idCount && bOK; i++) {
664 bOK = writeBMPInfoHeader(device, bmpHeaders[i]);
665 bOK &= (device->write(imageData[i]) == (int) imageData[i].size());
666 }
667 retValue = bOK;
668 }
669 }
670
671 delete [] entries;
672 delete [] bmpHeaders;
673 delete [] imageData;
674
675 }
676 return retValue;
677}
678
679/*!
680 Constructs an instance of QtIcoHandler initialized to use \a device.
681*/
682QtIcoHandler::QtIcoHandler(QIODevice *device)
683{
684 m_currentIconIndex = 0;
685 setDevice(device);
686 m_pICOReader = new ICOReader(device);
687}
688
689/*!
690 Destructor for QtIcoHandler.
691*/
693{
694 delete m_pICOReader;
695}
696
697QVariant QtIcoHandler::option(ImageOption option) const
698{
699 if (option == Size || option == ImageFormat) {
700 ICONDIRENTRY iconEntry;
701 if (m_pICOReader->readIconEntry(m_currentIconIndex, &iconEntry)) {
702 switch (option) {
703 case Size:
704 return QSize(iconEntry.bWidth ? iconEntry.bWidth : 256,
705 iconEntry.bHeight ? iconEntry.bHeight : 256);
706
707 case ImageFormat:
708 switch (iconEntry.wBitCount) {
709 case 2:
710 return QImage::Format_Mono;
711 case 24:
712 return QImage::Format_RGB32;
713 case 32:
714 return QImage::Format_ARGB32;
715 default:
716 return QImage::Format_Indexed8;
717 }
718 break;
719 default:
720 break;
721 }
722 }
723 }
724 return QVariant();
725}
726
727bool QtIcoHandler::supportsOption(ImageOption option) const
728{
729 return (option == Size || option == ImageFormat);
730}
731
732/*!
733 * Verifies if some values (magic bytes) are set as expected in the header of the file.
734 * If the magic bytes were found, it is assumed that the QtIcoHandler can read the file.
735 *
736 */
738{
739 if (knownCanRead)
740 return true;
741 bool bCanRead = false;
742 QIODevice *device = QImageIOHandler::device();
743 if (device) {
744 bCanRead = ICOReader::canRead(device);
745 if (bCanRead)
746 setFormat("ico");
747 } else {
748 qCWarning(lcIco, "QtIcoHandler::canRead() called with no device");
749 }
750 knownCanRead = bCanRead;
751 return bCanRead;
752}
753
754/*! This static function is used by the plugin code, and is provided for convenience only.
755 \a device must be an opened device with pointing to the start of the header data of the ICO file.
756*/
757bool QtIcoHandler::canRead(QIODevice *device)
758{
759 Q_ASSERT(device);
760 return ICOReader::canRead(device);
761}
762
763/*! \reimp
764
765*/
766bool QtIcoHandler::read(QImage *image)
767{
768 bool bSuccess = false;
769 QImage img = m_pICOReader->iconAt(m_currentIconIndex);
770
771 // Make sure we only write to \a image when we succeed.
772 if (!img.isNull()) {
773 bSuccess = true;
774 *image = img;
775 }
776
777 return bSuccess;
778}
779
780
781/*! \reimp
782
783*/
784bool QtIcoHandler::write(const QImage &image)
785{
786 QIODevice *device = QImageIOHandler::device();
787 QList<QImage> imgs;
788 imgs.append(image);
789 return ICOReader::write(device, imgs);
790}
791
792/*! \reimp
793
794*/
796{
797 return m_pICOReader->count();
798}
799
800/*! \reimp
801
802*/
803bool QtIcoHandler::jumpToImage(int imageNumber)
804{
805 if (imageNumber < imageCount()) {
806 m_currentIconIndex = imageNumber;
807 return true;
808 }
809
810 return false;
811}
812
813/*! \reimp
814
815*/
817{
818 return jumpToImage(m_currentIconIndex + 1);
819}
820
821QT_END_NAMESPACE
QImage iconAt(int index)
static QList< QImage > read(QIODevice *device)
Reads all the icons from the given device, and returns them as a list of QImage objects.
bool readIconEntry(int index, ICONDIRENTRY *iconEntry)
ICOReader(QIODevice *iodevice)
static bool write(QIODevice *device, const QList< QImage > &images)
Writes all the QImages in the images list to the given device.
static bool canRead(QIODevice *iodev)
The QtIcoHandler class provides support for the ICO image format.
Definition qicohandler.h:12
bool canRead() const override
Verifies if some values (magic bytes) are set as expected in the header of the file.
bool read(QImage *image) override
\reimp
virtual ~QtIcoHandler()
Destructor for QtIcoHandler.
bool supportsOption(ImageOption option) const override
Returns true if the QImageIOHandler supports the option option; otherwise returns false.
bool jumpToNextImage() override
\reimp
int imageCount() const override
\reimp
QVariant option(ImageOption option) const override
Returns the value assigned to option as a QVariant.
bool jumpToImage(int imageNumber) override
\reimp
bool write(const QImage &image) override
\reimp
QtIcoHandler(QIODevice *device)
Constructs an instance of QtIcoHandler initialized to use device.
static bool writeIconDir(QIODevice *iodev, const ICONDIR &iconDir)
#define ICONDIRENTRY_SIZE
#define BMP_INFOHDR_SIZE
static bool readIconDir(QIODevice *iodev, ICONDIR *iconDir)
static const char icoOrigDepthKey[]
static bool readIconDirEntry(QIODevice *iodev, ICONDIRENTRY *iconDirEntry)
static bool writeBMPInfoHeader(QIODevice *iodev, const BMP_INFOHDR &header)
static bool writeIconDirEntry(QIODevice *iodev, const ICONDIRENTRY &iconEntry)
#define ICONDIR_SIZE
static bool readBMPInfoHeader(QIODevice *iodev, BMP_INFOHDR *pHeader)