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 read16_24_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);
283 }
else if (icoAttrib.nbits == 16) {
288void ICOReader::readColorTable(QImage & image)
291 image.setColorCount(icoAttrib.ncolors);
293 for (
int i=0; i<icoAttrib.ncolors; i++) {
294 if (iod->read((
char*)rgb, 4) != 4) {
298 image.setColor(i, qRgb(rgb[2],rgb[1],rgb[0]));
307 if (icoAttrib.nbits == 1) {
309 }
else if (icoAttrib.nbits == 4) {
311 }
else if (icoAttrib.nbits == 8) {
313 }
else if (icoAttrib.nbits == 16 || icoAttrib.nbits == 24 || icoAttrib.nbits == 32 ) {
314 read16_24_32BMP(image);
320
321
322
323
324void ICOReader::read1BitBMP(QImage & image)
328 int h = image.height();
329 qsizetype bpl = image.bytesPerLine();
332 if (iod->read((
char*)image.scanLine(h),bpl) != bpl) {
342void ICOReader::read4BitBMP(QImage & image)
347 int buflen = ((icoAttrib.w+7)/8)*4;
348 uchar *buf =
new uchar[buflen];
352 if (iod->read((
char*)buf,buflen) != buflen) {
356 uchar *p = image.scanLine(h);
358 for (
int i=0; i<icoAttrib.w/2; i++) {
373void ICOReader::read8BitBMP(QImage & image)
378 qsizetype bpl = image.bytesPerLine();
381 if (iod->read((
char *)image.scanLine(h), bpl) != bpl) {
391void ICOReader::read16_24_32BMP(QImage & image)
397 uchar *buf =
new uchar[image.bytesPerLine()];
398 qsizetype bpl = ((qsizetype(icoAttrib.w)*icoAttrib.nbits+31)/32)*4;
402 p = (QRgb *)image.scanLine(h);
403 end = p + icoAttrib.w;
404 if (iod->read((
char *)buf, bpl) != bpl) {
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;
433 ICONDIRENTRY iconEntry;
436 static const uchar pngMagicData[] = { 137, 80, 78, 71, 13, 10, 26, 10 };
438 if (!iod->seek(iconEntry.dwImageOffset)
439 || iconEntry.dwBytesInRes > iod->bytesAvailable())
442 const QByteArray pngMagic = QByteArray::fromRawData((
const char*)pngMagicData,
sizeof(pngMagicData));
443 const bool isPngImage = (iod->read(pngMagic.size()) == pngMagic);
446 iod->seek(iconEntry.dwImageOffset);
447 QImage image = QImage::fromData(iod->read(iconEntry.dwBytesInRes),
"png");
448 image.setText(QLatin1String(icoOrigDepthKey), QString::number(iconEntry.wBitCount));
453 if (readBMPHeader(iconEntry.dwImageOffset, &header)) {
454 icoAttrib.nbits = header.biBitCount ? header.biBitCount : iconEntry.wBitCount;
456 switch (icoAttrib.nbits) {
460 icoAttrib.depth = 32;
473 if (icoAttrib.depth == 32)
474 icoAttrib.ncolors = 0;
476 icoAttrib.ncolors = header.biClrUsed ? uint(header.biClrUsed) : 1 << icoAttrib.nbits;
477 if (icoAttrib.ncolors > 256)
479 icoAttrib.w = iconEntry.bWidth;
480 if (icoAttrib.w == 0)
481 icoAttrib.w = header.biWidth;
482 icoAttrib.h = iconEntry.bHeight;
483 if (icoAttrib.h == 0)
484 icoAttrib.h = header.biHeight/2;
485 if (icoAttrib.w > 256 || icoAttrib.h > 256)
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;
497 const QSize size(icoAttrib.w, icoAttrib.h);
498 if (QImageIOHandler::allocateImage(size, format, &image)) {
499 findColorInfo(image);
500 if (!image.isNull()) {
502 if (!image.isNull()) {
503 if (icoAttrib.nbits == 32) {
504 img = std::move(image).convertToFormat(QImage::Format_ARGB32_Premultiplied);
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));
512 if (!mask.isNull()) {
514 img.setAlphaChannel(mask);
521 img.setText(QLatin1String(icoOrigDepthKey), QString::number(icoAttrib.nbits));
531
532
533
534
535
536
537
538
541 QList<QImage> images;
546 for (
int i = 0; i < N; i++)
547 images += reader.iconAt(i);
554
555
556
557
558
559
560
561
562
563
564
565bool ICOReader::write(QIODevice *device,
const QList<QImage> &images)
567 bool retValue =
false;
571 qint64 origOffset = device->pos();
576 id.idCount = images.size();
578 ICONDIRENTRY * entries =
new ICONDIRENTRY[id.idCount];
579 BMP_INFOHDR * bmpHeaders =
new BMP_INFOHDR[id.idCount];
580 QByteArray * imageData =
new QByteArray[id.idCount];
582 for (
int i=0; i<id.idCount; i++) {
584 QImage image = images[i];
587 if (image.width() > 256 || image.height() > 256)
589 image = image.scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation);
591 QImage maskImage(image.width(), image.height(), QImage::Format_Mono);
592 image = image.convertToFormat(QImage::Format_ARGB32);
593 maskImage.fill(Qt::color1);
596 int bpl_bmp = ((image.width()*nbits+31)/32)*4;
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;
602 entries[i].bWidth = image.width() < 256 ? image.width() : 0;
604 + (maskImage.bytesPerLine() * maskImage.height());
605 entries[i].wPlanes = 1;
610 entries[i].dwImageOffset = entries[i-1].dwImageOffset + entries[i-1].dwBytesInRes;
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;
617 bmpHeaders[i].biPlanes = entries[i].wPlanes;
620 bmpHeaders[i].biWidth = entries[i].bWidth ? entries[i].bWidth : 256;
621 bmpHeaders[i].biXPelsPerMeter = 0;
622 bmpHeaders[i].biYPelsPerMeter = 0;
624 QBuffer buffer(&imageData[i]);
625 buffer.open(QIODevice::WriteOnly);
627 uchar *buf =
new uchar[bpl_bmp];
629 memset( buf, 0, bpl_bmp );
631 for (y = image.height() - 1; y >= 0; y--) {
633 QRgb *p = (QRgb *)image.scanLine(y);
634 QRgb *end = p + image.width();
643 maskImage.setPixel(x, y, 0);
647 buffer.write((
char*)buf, bpl_bmp);
652 for (y = maskImage.height() - 1; y >= 0; y--)
653 buffer.write((
char*)maskImage.scanLine(y), maskImage.bytesPerLine());
656 if (writeIconDir(device, id)) {
659 for (i = 0; i < id.idCount && bOK; i++) {
660 bOK = writeIconDirEntry(device, entries[i]);
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());
672 delete [] bmpHeaders;
680
681
684 m_currentIconIndex = 0;
690
691
699 if (option == Size || option == ImageFormat) {
700 ICONDIRENTRY iconEntry;
704 return QSize(iconEntry.bWidth ? iconEntry.bWidth : 256,
705 iconEntry.bHeight ? iconEntry.bHeight : 256);
708 switch (iconEntry.wBitCount) {
710 return QImage::Format_Mono;
712 return QImage::Format_RGB32;
714 return QImage::Format_ARGB32;
716 return QImage::Format_Indexed8;
729 return (option == Size || option == ImageFormat);
733
734
735
736
741 bool bCanRead =
false;
742 QIODevice *device = QImageIOHandler::device();
744 bCanRead = ICOReader::canRead(device);
748 qCWarning(lcIco,
"QtIcoHandler::canRead() called with no device");
750 knownCanRead = bCanRead;
755
756
760 return ICOReader::canRead(device);
764
765
768 bool bSuccess =
false;
769 QImage img = m_pICOReader->iconAt(m_currentIconIndex);
782
783
786 QIODevice *device = QImageIOHandler::device();
789 return ICOReader::write(device, imgs);
793
794
801
802
806 m_currentIconIndex = imageNumber;
814
815
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.
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]