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 read24_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 }
284}
285
286void ICOReader::readColorTable(QImage & image)
287{
288 if (iod) {
289 image.setColorCount(icoAttrib.ncolors);
290 uchar rgb[4];
291 for (int i=0; i<icoAttrib.ncolors; i++) {
292 if (iod->read((char*)rgb, 4) != 4) {
293 image = QImage();
294 break;
295 }
296 image.setColor(i, qRgb(rgb[2],rgb[1],rgb[0]));
297 }
298 } else {
299 image = QImage();
300 }
301}
302
303void ICOReader::readBMP(QImage & image)
304{
305 if (icoAttrib.nbits == 1) { // 1 bit BMP image
306 read1BitBMP(image);
307 } else if (icoAttrib.nbits == 4) { // 4 bit BMP image
308 read4BitBMP(image);
309 } else if (icoAttrib.nbits == 8) {
310 read8BitBMP(image);
311 } else if (icoAttrib.nbits == 24 || icoAttrib.nbits == 32) { // 24,32 bit BMP image
312 read24_32BMP(image);
313 }
314}
315
316
317/**
318 * NOTE: A 1 bit BMP is only flipped vertically, and not horizontally like all other color depths!
319 * (This is the same with the bitmask)
320 *
321 */
322void ICOReader::read1BitBMP(QImage & image)
323{
324 if (iod) {
325
326 int h = image.height();
327 qsizetype bpl = image.bytesPerLine();
328
329 while (--h >= 0) {
330 if (iod->read((char*)image.scanLine(h),bpl) != bpl) {
331 image = QImage();
332 break;
333 }
334 }
335 } else {
336 image = QImage();
337 }
338}
339
340void ICOReader::read4BitBMP(QImage & image)
341{
342 if (iod) {
343
344 int h = icoAttrib.h;
345 int buflen = ((icoAttrib.w+7)/8)*4;
346 uchar *buf = new uchar[buflen];
347 Q_CHECK_PTR(buf);
348
349 while (--h >= 0) {
350 if (iod->read((char*)buf,buflen) != buflen) {
351 image = QImage();
352 break;
353 }
354 uchar *p = image.scanLine(h);
355 uchar *b = buf;
356 for (int i=0; i<icoAttrib.w/2; i++) { // convert nibbles to bytes
357 *p++ = *b >> 4;
358 *p++ = *b++ & 0x0f;
359 }
360 if (icoAttrib.w & 1) // the last nibble
361 *p = *b >> 4;
362 }
363
364 delete [] buf;
365
366 } else {
367 image = QImage();
368 }
369}
370
371void ICOReader::read8BitBMP(QImage & image)
372{
373 if (iod) {
374
375 int h = icoAttrib.h;
376 qsizetype bpl = image.bytesPerLine();
377
378 while (--h >= 0) {
379 if (iod->read((char *)image.scanLine(h), bpl) != bpl) {
380 image = QImage();
381 break;
382 }
383 }
384 } else {
385 image = QImage();
386 }
387}
388
389void ICOReader::read24_32BMP(QImage &image)
390{
391 if (iod) {
392 int h = icoAttrib.h;
393 QRgb *p;
394 QRgb *end;
395 uchar *buf = new uchar[image.bytesPerLine()];
396 qsizetype bpl = ((qsizetype(icoAttrib.w)*icoAttrib.nbits+31)/32)*4;
397 uchar *b;
398
399 while (--h >= 0) {
400 p = (QRgb *)image.scanLine(h);
401 end = p + icoAttrib.w;
402 if (iod->read((char *)buf, bpl) != bpl) {
403 image = QImage();
404 break;
405 }
406 b = buf;
407 while (p < end) {
408 if (icoAttrib.nbits == 24)
409 *p++ = qRgb(*(b+2), *(b+1), *b);
410 else if (icoAttrib.nbits == 32)
411 *p++ = qRgba(*(b+2), *(b+1), *b, *(b+3));
412 b += icoAttrib.nbits/8;
413 }
414 }
415
416 delete[] buf;
417
418 } else {
419 image = QImage();
420 }
421}
422
423static const char icoOrigDepthKey[] = "_q_icoOrigDepth";
424
426{
427 QImage img;
428
429 if (count() > index) { // forces header to be read
430
431 ICONDIRENTRY iconEntry;
432 if (readIconEntry(index, &iconEntry)) {
433
434 static const uchar pngMagicData[] = { 137, 80, 78, 71, 13, 10, 26, 10 };
435
436 if (!iod->seek(startpos + iconEntry.dwImageOffset)
437 || iconEntry.dwBytesInRes > iod->bytesAvailable())
438 return img;
439
440 const QByteArray pngMagic = QByteArray::fromRawData((const char*)pngMagicData, sizeof(pngMagicData));
441 const bool isPngImage = (iod->read(pngMagic.size()) == pngMagic);
442
443 if (isPngImage) {
444 iod->seek(startpos + iconEntry.dwImageOffset);
445 QImage image = QImage::fromData(iod->read(iconEntry.dwBytesInRes), "png");
446 image.setText(QLatin1String(icoOrigDepthKey), QString::number(iconEntry.wBitCount));
447 return image;
448 }
449
450 BMP_INFOHDR header;
451 if (readBMPHeader(iconEntry.dwImageOffset, &header)) {
452 icoAttrib.nbits = header.biBitCount ? header.biBitCount : iconEntry.wBitCount;
453
454 switch (icoAttrib.nbits) {
455 case 32:
456 case 24:
457 icoAttrib.depth = 32;
458 break;
459 // NB: 15/16 bpp BMP decoding is not implemented, so such images are rejected here
460 case 8:
461 case 4:
462 icoAttrib.depth = 8;
463 break;
464 case 1:
465 icoAttrib.depth = 1;
466 break;
467 default:
468 return img;
469 break;
470 }
471 if (icoAttrib.depth == 32) // there's no colormap
472 icoAttrib.ncolors = 0;
473 else // # colors used
474 icoAttrib.ncolors = header.biClrUsed ? uint(header.biClrUsed) : 1 << icoAttrib.nbits;
475 if (icoAttrib.ncolors > 256) //color table can't be more than 256
476 return img;
477 icoAttrib.w = iconEntry.bWidth;
478 if (icoAttrib.w == 0) // means 256 pixels
479 icoAttrib.w = header.biWidth;
480 icoAttrib.h = iconEntry.bHeight;
481 if (icoAttrib.h == 0) // means 256 pixels
482 icoAttrib.h = header.biHeight/2;
483 if (icoAttrib.w > 256 || icoAttrib.h > 256) // Max ico size
484 return img;
485
486 QImage::Format format = QImage::Format_ARGB32;
487 if (icoAttrib.nbits == 24)
488 format = QImage::Format_RGB32;
489 else if (icoAttrib.ncolors == 2 && icoAttrib.depth == 1)
490 format = QImage::Format_Mono;
491 else if (icoAttrib.ncolors > 0)
492 format = QImage::Format_Indexed8;
493
494 QImage image;
495 const QSize size(icoAttrib.w, icoAttrib.h);
496 if (QImageIOHandler::allocateImage(size, format, &image)) {
497 findColorInfo(image);
498 if (!image.isNull()) {
499 readBMP(image);
500 if (!image.isNull()) {
501 if (icoAttrib.nbits == 32) {
502 img = std::move(image).convertToFormat(QImage::Format_ARGB32_Premultiplied);
503 } else {
504 QImage mask(image.width(), image.height(), QImage::Format_Mono);
505 if (!mask.isNull()) {
506 mask.setColorCount(2);
507 mask.setColor(0, qRgba(255,255,255,0xff));
508 mask.setColor(1, qRgba(0 ,0 ,0 ,0xff));
509 read1BitBMP(mask);
510 if (!mask.isNull()) {
511 img = image;
512 img.setAlphaChannel(mask);
513 }
514 }
515 }
516 }
517 }
518 }
519 img.setText(QLatin1String(icoOrigDepthKey), QString::number(icoAttrib.nbits));
520 }
521 }
522 }
523
524 return img;
525}
526
527
528/*!
529 Reads all the icons from the given \a device, and returns them as
530 a list of QImage objects.
531
532 Each image has an alpha channel that represents the mask from the
533 corresponding icon.
534
535 \sa write()
536*/
537QList<QImage> ICOReader::read(QIODevice *device)
538{
539 QList<QImage> images;
540
541 ICOReader reader(device);
542 const int N = reader.count();
543 images.reserve(N);
544 for (int i = 0; i < N; i++)
545 images += reader.iconAt(i);
546
547 return images;
548}
549
550
551/*!
552 Writes all the QImages in the \a images list to the given \a
553 device. Returns \c true if the images are written successfully;
554 otherwise returns \c false.
555
556 The first image in the list is stored as the first icon in the
557 device, and is therefore used as the default icon by applications.
558 The alpha channel of each image is converted to a mask for each
559 corresponding icon.
560
561 \sa read()
562*/
563bool ICOReader::write(QIODevice *device, const QList<QImage> &images)
564{
565 bool retValue = false;
566
567 if (images.size()) {
568
569 qint64 origOffset = device->pos();
570
571 ICONDIR id = { };
572 id.idReserved = 0;
573 id.idType = 1;
574 id.idCount = images.size();
575
576 ICONDIRENTRY * entries = new ICONDIRENTRY[id.idCount];
577 BMP_INFOHDR * bmpHeaders = new BMP_INFOHDR[id.idCount];
578 QByteArray * imageData = new QByteArray[id.idCount];
579
580 for (int i=0; i<id.idCount; i++) {
581
582 QImage image = images[i];
583 // Scale down the image if it is larger than 256 pixels in either width or height
584 // because this is a maximum size of image in the ICO file.
585 if (image.width() > 256 || image.height() > 256)
586 {
587 image = image.scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation);
588 }
589 QImage maskImage(image.width(), image.height(), QImage::Format_Mono);
590 image = image.convertToFormat(QImage::Format_ARGB32);
591 maskImage.fill(Qt::color1);
592
593 int nbits = 32;
594 int bpl_bmp = ((image.width()*nbits+31)/32)*4;
595
596 entries[i].bColorCount = 0;
597 entries[i].bReserved = 0;
598 entries[i].wBitCount = nbits;
599 entries[i].bHeight = image.height() < 256 ? image.height() : 0; // 0 means 256
600 entries[i].bWidth = image.width() < 256 ? image.width() : 0; // 0 means 256
601 entries[i].dwBytesInRes = BMP_INFOHDR_SIZE + (bpl_bmp * image.height())
602 + (maskImage.bytesPerLine() * maskImage.height());
603 entries[i].wPlanes = 1;
604 if (i == 0)
605 entries[i].dwImageOffset = origOffset + ICONDIR_SIZE
606 + (id.idCount * ICONDIRENTRY_SIZE);
607 else
608 entries[i].dwImageOffset = entries[i-1].dwImageOffset + entries[i-1].dwBytesInRes;
609
610 bmpHeaders[i].biBitCount = entries[i].wBitCount;
611 bmpHeaders[i].biClrImportant = 0;
612 bmpHeaders[i].biClrUsed = entries[i].bColorCount;
613 bmpHeaders[i].biCompression = 0;
614 bmpHeaders[i].biHeight = entries[i].bHeight ? entries[i].bHeight * 2 : 256 * 2; // 2 is for the mask
615 bmpHeaders[i].biPlanes = entries[i].wPlanes;
616 bmpHeaders[i].biSize = BMP_INFOHDR_SIZE;
617 bmpHeaders[i].biSizeImage = entries[i].dwBytesInRes - BMP_INFOHDR_SIZE;
618 bmpHeaders[i].biWidth = entries[i].bWidth ? entries[i].bWidth : 256;
619 bmpHeaders[i].biXPelsPerMeter = 0;
620 bmpHeaders[i].biYPelsPerMeter = 0;
621
622 QBuffer buffer(&imageData[i]);
623 buffer.open(QIODevice::WriteOnly);
624
625 uchar *buf = new uchar[bpl_bmp];
626 uchar *b;
627 memset( buf, 0, bpl_bmp );
628 int y;
629 for (y = image.height() - 1; y >= 0; y--) { // write the image bits
630 // 32 bits
631 QRgb *p = (QRgb *)image.scanLine(y);
632 QRgb *end = p + image.width();
633 b = buf;
634 int x = 0;
635 while (p < end) {
636 *b++ = qBlue(*p);
637 *b++ = qGreen(*p);
638 *b++ = qRed(*p);
639 *b++ = qAlpha(*p);
640 if (qAlpha(*p) > 0) // Even mostly transparent pixels must not be masked away
641 maskImage.setPixel(x, y, 0);
642 p++;
643 x++;
644 }
645 buffer.write((char*)buf, bpl_bmp);
646 }
647 delete[] buf;
648
649 // NOTE! !! The mask is only flipped vertically - not horizontally !!
650 for (y = maskImage.height() - 1; y >= 0; y--)
651 buffer.write((char*)maskImage.scanLine(y), maskImage.bytesPerLine());
652 }
653
654 if (writeIconDir(device, id)) {
655 int i;
656 bool bOK = true;
657 for (i = 0; i < id.idCount && bOK; i++) {
658 bOK = writeIconDirEntry(device, entries[i]);
659 }
660 if (bOK) {
661 for (i = 0; i < id.idCount && bOK; i++) {
662 bOK = writeBMPInfoHeader(device, bmpHeaders[i]);
663 bOK &= (device->write(imageData[i]) == (int) imageData[i].size());
664 }
665 retValue = bOK;
666 }
667 }
668
669 delete [] entries;
670 delete [] bmpHeaders;
671 delete [] imageData;
672
673 }
674 return retValue;
675}
676
677/*!
678 Constructs an instance of QtIcoHandler initialized to use \a device.
679*/
680QtIcoHandler::QtIcoHandler(QIODevice *device)
681{
682 m_currentIconIndex = 0;
683 setDevice(device);
684 m_pICOReader = new ICOReader(device);
685}
686
687/*!
688 Destructor for QtIcoHandler.
689*/
691{
692 delete m_pICOReader;
693}
694
695QVariant QtIcoHandler::option(ImageOption option) const
696{
697 if (option == Size || option == ImageFormat) {
698 ICONDIRENTRY iconEntry;
699 if (m_pICOReader->readIconEntry(m_currentIconIndex, &iconEntry)) {
700 switch (option) {
701 case Size:
702 return QSize(iconEntry.bWidth ? iconEntry.bWidth : 256,
703 iconEntry.bHeight ? iconEntry.bHeight : 256);
704
705 case ImageFormat:
706 switch (iconEntry.wBitCount) {
707 case 2:
708 return QImage::Format_Mono;
709 case 24:
710 return QImage::Format_RGB32;
711 case 32:
712 return QImage::Format_ARGB32;
713 default:
714 return QImage::Format_Indexed8;
715 }
716 break;
717 default:
718 break;
719 }
720 }
721 }
722 return QVariant();
723}
724
725bool QtIcoHandler::supportsOption(ImageOption option) const
726{
727 return (option == Size || option == ImageFormat);
728}
729
730/*!
731 * Verifies if some values (magic bytes) are set as expected in the header of the file.
732 * If the magic bytes were found, it is assumed that the QtIcoHandler can read the file.
733 *
734 */
736{
737 if (knownCanRead)
738 return true;
739 bool bCanRead = false;
740 QIODevice *device = QImageIOHandler::device();
741 if (device) {
742 bCanRead = ICOReader::canRead(device);
743 if (bCanRead)
744 setFormat("ico");
745 } else {
746 qCWarning(lcIco, "QtIcoHandler::canRead() called with no device");
747 }
748 knownCanRead = bCanRead;
749 return bCanRead;
750}
751
752/*! This static function is used by the plugin code, and is provided for convenience only.
753 \a device must be an opened device with pointing to the start of the header data of the ICO file.
754*/
755bool QtIcoHandler::canRead(QIODevice *device)
756{
757 Q_ASSERT(device);
758 return ICOReader::canRead(device);
759}
760
761/*! \reimp
762
763*/
764bool QtIcoHandler::read(QImage *image)
765{
766 bool bSuccess = false;
767 QImage img = m_pICOReader->iconAt(m_currentIconIndex);
768
769 // Make sure we only write to \a image when we succeed.
770 if (!img.isNull()) {
771 bSuccess = true;
772 *image = img;
773 }
774
775 return bSuccess;
776}
777
778
779/*! \reimp
780
781*/
782bool QtIcoHandler::write(const QImage &image)
783{
784 QIODevice *device = QImageIOHandler::device();
785 QList<QImage> imgs;
786 imgs.append(image);
787 return ICOReader::write(device, imgs);
788}
789
790/*! \reimp
791
792*/
794{
795 return m_pICOReader->count();
796}
797
798/*! \reimp
799
800*/
801bool QtIcoHandler::jumpToImage(int imageNumber)
802{
803 if (imageNumber < imageCount()) {
804 m_currentIconIndex = imageNumber;
805 return true;
806 }
807
808 return false;
809}
810
811/*! \reimp
812
813*/
815{
816 return jumpToImage(m_currentIconIndex + 1);
817}
818
819QT_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:13
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.
Combined button and popup list for selecting options.
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)