File indexing completed on 2024-04-28 04:58:03

0001 /*
0002     icoutils_common.cpp - Extract Microsoft Window icons and images using icoutils package
0003 
0004     SPDX-FileCopyrightText: 2009-2010 Pali Rohár <pali.rohar@gmail.com>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "icoutils.h"
0010 
0011 #include <QBuffer>
0012 #include <QImage>
0013 #include <QImageReader>
0014 #include <QList>
0015 #include <QString>
0016 #include <QTemporaryFile>
0017 
0018 #include <algorithm>
0019 
0020 qreal distance(int width, int height, int desiredWidth, int desiredHeight, int depth)
0021 {
0022     // We want as high of a depth as possible (32-bit)
0023     auto targetSamples = desiredWidth * desiredHeight * 32;
0024     auto xscale = (1.0 * desiredWidth) / width;
0025     auto yscale = (1.0 * desiredHeight) / height;
0026 
0027     // clamp to the lower of the two scales
0028     // also clamp to one, as scaling up adds no effective
0029     // samples, only interpolated samples
0030     auto sampleScale = std::min(1.0, std::min(xscale, yscale));
0031 
0032     // number of effective source samples in the target
0033     auto effectiveSamples = width * height * sampleScale * sampleScale * depth;
0034     // scale down another time, to account for loss of fidelity when
0035     // using a downscaled image, biases towards smaller downscaling ratios
0036     effectiveSamples *= sampleScale;
0037 
0038     return targetSamples - effectiveSamples;
0039 }
0040 
0041 bool IcoUtils::loadIcoImageFromExe(QIODevice *inputDevice, QImage &image, int needWidth, int needHeight)
0042 {
0043     QTemporaryFile inputFile;
0044 
0045     if (!inputFile.open())
0046         return false;
0047 
0048     QByteArray data = inputDevice->readAll();
0049 
0050     if (inputFile.write(data) == -1)
0051         return false;
0052 
0053     return IcoUtils::loadIcoImageFromExe(inputFile.fileName(), image, needWidth, needHeight);
0054 }
0055 
0056 bool IcoUtils::loadIcoImageFromExe(const QString &inputFileName, QImage &image, int needWidth, int needHeight)
0057 {
0058     QBuffer iconData;
0059     if (!iconData.open(QIODevice::ReadWrite)) {
0060         return false;
0061     }
0062 
0063     if (!IcoUtils::loadIcoImageFromExe(inputFileName, &iconData))
0064         return false;
0065 
0066     if (!iconData.seek(0)) {
0067         return false;
0068     }
0069 
0070     return IcoUtils::loadIcoImage(&iconData, image, needWidth, needHeight);
0071 }
0072 
0073 bool IcoUtils::loadIcoImage(QImageReader &reader, QImage &image, int needWidth, int needHeight)
0074 {
0075     // QTBUG-70812: for files with incorrect bits per pixel, QImageReader::canRead() returns
0076     // false but it can still correctly determine the imageCount() and read the icon just fine.
0077     if (reader.imageCount() == 0) {
0078         return false;
0079     }
0080 
0081     QList<QImage> icons;
0082     do
0083         icons << reader.read();
0084     while (reader.jumpToNextImage());
0085 
0086     if (icons.empty())
0087         return false;
0088 
0089     int index = icons.size() - 1;
0090     qreal best = std::numeric_limits<qreal>::max();
0091 
0092     for (int i = 0; i < icons.size(); ++i) {
0093         const QImage &icon = icons.at(i);
0094 
0095         // QtIcoHandler converts all images to 32-bit depth,
0096         // but it stores the actual depth of the icon extracted in custom text:
0097         // qtbase/src/plugins/imageformats/ico/qicohandler.cpp:455
0098         int depth = icon.text(QStringLiteral("_q_icoOrigDepth")).toInt();
0099         if (depth == 0 || depth > 32) {
0100             depth = icon.depth();
0101         }
0102 
0103         const qreal dist = distance(icon.width(), icon.height(), needWidth, needHeight, depth);
0104 
0105         if (dist < best) {
0106             index = i;
0107             best = dist;
0108         }
0109     }
0110 
0111     image = icons.at(index);
0112     return true;
0113 }
0114 
0115 bool IcoUtils::loadIcoImage(QIODevice *inputDevice, QImage &image, int needWidth, int needHeight)
0116 {
0117     QImageReader reader(inputDevice, "ico");
0118     return IcoUtils::loadIcoImage(reader, image, needWidth, needHeight);
0119 }
0120 
0121 bool IcoUtils::loadIcoImage(const QString &inputFileName, QImage &image, int needWidth, int needHeight)
0122 {
0123     QImageReader reader(inputFileName, "ico");
0124     return IcoUtils::loadIcoImage(reader, image, needWidth, needHeight);
0125 }