File indexing completed on 2024-11-10 04:30:40
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 }