source: trunk/src/plugins/imageformats/ico/qicohandler.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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