6
7
8
9
10
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>
25Q_LOGGING_CATEGORY(lcIco,
"qt.gui.imageio.ico")
41} ICONDIRENTRY, *LPICONDIRENTRY;
42#define ICONDIRENTRY_SIZE 16
65} BMP_INFOHDR ,*LPBMP_INFOHDR;
66#define BMP_INFOHDR_SIZE 40
80 static bool write(QIODevice *device,
const QList<QImage> &images);
87 bool readBMPHeader(quint32 imageOffset, BMP_INFOHDR * header);
88 void findColorInfo(QImage & image);
89 void readColorTable(QImage & image);
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);
138 return iodev->write((
char*)&iconDir, 6) == 6;
168 return iconDir.idCount;
174 bool isProbablyICO =
false;
176 qint64 oldPos = iodev->pos();
179 if (readIconDir(iodev, &ikonDir)) {
180 if (readIconDirEntry(iodev, &ikonDir.idEntries[0])) {
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)
187 && ikonDir.idEntries[0].dwBytesInRes >= 40
189 isProbablyICO =
true;
192 if (iodev->isSequential()) {
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);
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);
206 tmp = ikonDir.idEntries[0].wBitCount;
207 iodev->ungetChar((tmp >> 8) & 0xff);
208 iodev->ungetChar(tmp & 0xff);
210 tmp = ikonDir.idEntries[0].wPlanes;
211 iodev->ungetChar((tmp >> 8) & 0xff);
212 iodev->ungetChar(tmp & 0xff);
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);
221 if (iodev->isSequential()) {
223 quint32 tmp = ikonDir.idCount;
224 iodev->ungetChar((tmp >> 8) & 0xff);
225 iodev->ungetChar(tmp & 0xff);
227 tmp = ikonDir.idType;
228 iodev->ungetChar((tmp >> 8) & 0xff);
229 iodev->ungetChar(tmp & 0xff);
231 tmp = ikonDir.idReserved;
232 iodev->ungetChar((tmp >> 8) & 0xff);
233 iodev->ungetChar(tmp & 0xff);
236 if (!iodev->isSequential()) iodev->seek(oldPos);
239 return isProbablyICO;
244 if (iod && !headerRead) {
245 startpos = iod->pos();
246 if (readIconDir(iod, &iconDir)) {
247 if (iconDir.idReserved == 0 && (iconDir.idType == 1 || iconDir.idType == 2))
259 return readIconDirEntry(iod, iconEntry);
267bool ICOReader::readBMPHeader(quint32 imageOffset, BMP_INFOHDR * header)
270 if (iod->seek(startpos + imageOffset)) {
271 if (readBMPInfoHeader(iod, header)) {
279void ICOReader::findColorInfo(QImage & image)
281 if (icoAttrib.ncolors > 0) {
282 readColorTable(image);
286void ICOReader::readColorTable(QImage & image)
289 image.setColorCount(icoAttrib.ncolors);
291 for (
int i=0; i<icoAttrib.ncolors; i++) {
292 if (iod->read((
char*)rgb, 4) != 4) {
296 image.setColor(i, qRgb(rgb[2],rgb[1],rgb[0]));
305 if (icoAttrib.nbits == 1) {
307 }
else if (icoAttrib.nbits == 4) {
309 }
else if (icoAttrib.nbits == 8) {
311 }
else if (icoAttrib.nbits == 24 || icoAttrib.nbits == 32) {
318
319
320
321
322void ICOReader::read1BitBMP(QImage & image)
326 int h = image.height();
327 qsizetype bpl = image.bytesPerLine();
330 if (iod->read((
char*)image.scanLine(h),bpl) != bpl) {
340void ICOReader::read4BitBMP(QImage & image)
345 int buflen = ((icoAttrib.w+7)/8)*4;
346 uchar *buf =
new uchar[buflen];
350 if (iod->read((
char*)buf,buflen) != buflen) {
354 uchar *p = image.scanLine(h);
356 for (
int i=0; i<icoAttrib.w/2; i++) {
371void ICOReader::read8BitBMP(QImage & image)
376 qsizetype bpl = image.bytesPerLine();
379 if (iod->read((
char *)image.scanLine(h), bpl) != bpl) {
389void ICOReader::read24_32BMP(QImage &image)
395 uchar *buf =
new uchar[image.bytesPerLine()];
396 qsizetype bpl = ((qsizetype(icoAttrib.w)*icoAttrib.nbits+31)/32)*4;
400 p = (QRgb *)image.scanLine(h);
401 end = p + icoAttrib.w;
402 if (iod->read((
char *)buf, bpl) != bpl) {
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;
431 ICONDIRENTRY iconEntry;
434 static const uchar pngMagicData[] = { 137, 80, 78, 71, 13, 10, 26, 10 };
436 if (!iod->seek(startpos + iconEntry.dwImageOffset)
437 || iconEntry.dwBytesInRes > iod->bytesAvailable())
440 const QByteArray pngMagic = QByteArray::fromRawData((
const char*)pngMagicData,
sizeof(pngMagicData));
441 const bool isPngImage = (iod->read(pngMagic.size()) == pngMagic);
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));
451 if (readBMPHeader(iconEntry.dwImageOffset, &header)) {
452 icoAttrib.nbits = header.biBitCount ? header.biBitCount : iconEntry.wBitCount;
454 switch (icoAttrib.nbits) {
457 icoAttrib.depth = 32;
471 if (icoAttrib.depth == 32)
472 icoAttrib.ncolors = 0;
474 icoAttrib.ncolors = header.biClrUsed ? uint(header.biClrUsed) : 1 << icoAttrib.nbits;
475 if (icoAttrib.ncolors > 256)
477 icoAttrib.w = iconEntry.bWidth;
478 if (icoAttrib.w == 0)
479 icoAttrib.w = header.biWidth;
480 icoAttrib.h = iconEntry.bHeight;
481 if (icoAttrib.h == 0)
482 icoAttrib.h = header.biHeight/2;
483 if (icoAttrib.w > 256 || icoAttrib.h > 256)
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;
495 const QSize size(icoAttrib.w, icoAttrib.h);
496 if (QImageIOHandler::allocateImage(size, format, &image)) {
497 findColorInfo(image);
498 if (!image.isNull()) {
500 if (!image.isNull()) {
501 if (icoAttrib.nbits == 32) {
502 img =
std::move(image).convertToFormat(QImage::Format_ARGB32_Premultiplied);
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));
510 if (!mask.isNull()) {
512 img.setAlphaChannel(mask);
519 img.setText(QLatin1String(
icoOrigDepthKey), QString::number(icoAttrib.nbits));
529
530
531
532
533
534
535
536
539 QList<QImage> images;
544 for (
int i = 0; i < N; i++)
545 images += reader.iconAt(i);
552
553
554
555
556
557
558
559
560
561
562
563bool ICOReader::write(QIODevice *device,
const QList<QImage> &images)
565 bool retValue =
false;
569 qint64 origOffset = device->pos();
574 id.idCount = images.size();
576 ICONDIRENTRY * entries =
new ICONDIRENTRY[id.idCount];
577 BMP_INFOHDR * bmpHeaders =
new BMP_INFOHDR[id.idCount];
578 QByteArray * imageData =
new QByteArray[id.idCount];
580 for (
int i=0; i<id.idCount; i++) {
582 QImage image = images[i];
585 if (image.width() > 256 || image.height() > 256)
587 image = image.scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation);
589 QImage maskImage(image.width(), image.height(), QImage::Format_Mono);
590 image = image.convertToFormat(QImage::Format_ARGB32);
591 maskImage.fill(Qt::color1);
594 int bpl_bmp = ((image.width()*nbits+31)/32)*4;
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;
600 entries[i].bWidth = image.width() < 256 ? image.width() : 0;
602 + (maskImage.bytesPerLine() * maskImage.height());
603 entries[i].wPlanes = 1;
608 entries[i].dwImageOffset = entries[i-1].dwImageOffset + entries[i-1].dwBytesInRes;
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;
615 bmpHeaders[i].biPlanes = entries[i].wPlanes;
618 bmpHeaders[i].biWidth = entries[i].bWidth ? entries[i].bWidth : 256;
619 bmpHeaders[i].biXPelsPerMeter = 0;
620 bmpHeaders[i].biYPelsPerMeter = 0;
622 QBuffer buffer(&imageData[i]);
623 buffer.open(QIODevice::WriteOnly);
625 uchar *buf =
new uchar[bpl_bmp];
627 memset( buf, 0, bpl_bmp );
629 for (y = image.height() - 1; y >= 0; y--) {
631 QRgb *p = (QRgb *)image.scanLine(y);
632 QRgb *end = p + image.width();
641 maskImage.setPixel(x, y, 0);
645 buffer.write((
char*)buf, bpl_bmp);
650 for (y = maskImage.height() - 1; y >= 0; y--)
651 buffer.write((
char*)maskImage.scanLine(y), maskImage.bytesPerLine());
654 if (writeIconDir(device, id)) {
657 for (i = 0; i < id.idCount && bOK; i++) {
658 bOK = writeIconDirEntry(device, entries[i]);
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());
670 delete [] bmpHeaders;
678
679
682 m_currentIconIndex = 0;
688
689
697 if (option == Size || option == ImageFormat) {
698 ICONDIRENTRY iconEntry;
702 return QSize(iconEntry.bWidth ? iconEntry.bWidth : 256,
703 iconEntry.bHeight ? iconEntry.bHeight : 256);
706 switch (iconEntry.wBitCount) {
708 return QImage::Format_Mono;
710 return QImage::Format_RGB32;
712 return QImage::Format_ARGB32;
714 return QImage::Format_Indexed8;
727 return (option == Size || option == ImageFormat);
731
732
733
734
739 bool bCanRead =
false;
740 QIODevice *device = QImageIOHandler::device();
742 bCanRead = ICOReader::canRead(device);
746 qCWarning(lcIco,
"QtIcoHandler::canRead() called with no device");
748 knownCanRead = bCanRead;
753
754
758 return ICOReader::canRead(device);
762
763
766 bool bSuccess =
false;
767 QImage img = m_pICOReader->iconAt(m_currentIconIndex);
780
781
784 QIODevice *device = QImageIOHandler::device();
787 return ICOReader::write(device, imgs);
791
792
799
800
804 m_currentIconIndex = imageNumber;
812
813
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.
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
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)
static bool readBMPInfoHeader(QIODevice *iodev, BMP_INFOHDR *pHeader)
quint32_le biYPelsPerMeter
quint32_le biClrImportant
quint32_le biXPelsPerMeter
ICONDIRENTRY idEntries[1]