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

source code of qtbase/src/plugins/imageformats/ico/qicohandler.cpp