1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
/*
icoutils_common.cpp - Extract Microsoft Window icons and images using icoutils package
SPDX-FileCopyrightText: 2009-2010 Pali Rohár <pali.rohar@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "icoutils.h"
#include <QBuffer>
#include <QList>
#include <QString>
#include <QTemporaryFile>
#include <QImage>
#include <QImageReader>
#include <algorithm>
qreal distance(int width, int height, int desiredWidth, int desiredHeight, int depth)
{
// We want as high of a depth as possible (32-bit)
auto targetSamples = desiredWidth * desiredHeight * 32;
auto xscale = (1.0 * desiredWidth) / width;
auto yscale = (1.0 * desiredHeight) / height;
// clamp to the lower of the two scales
// also clamp to one, as scaling up adds no effective
// samples, only interpolated samples
auto sampleScale = std::min(1.0, std::min(xscale, yscale));
// number of effective source samples in the target
auto effectiveSamples = width * height * sampleScale * sampleScale * depth;
// scale down another time, to account for loss of fidelity when
// using a downscaled image, biases towards smaller downscaling ratios
effectiveSamples *= sampleScale;
return targetSamples - effectiveSamples;
}
bool IcoUtils::loadIcoImageFromExe(QIODevice * inputDevice, QImage &image, int needWidth, int needHeight)
{
QTemporaryFile inputFile;
if ( ! inputFile.open() )
return false;
QByteArray data = inputDevice->readAll();
if ( inputFile.write(data) == -1 )
return false;
return IcoUtils::loadIcoImageFromExe(inputFile.fileName(), image, needWidth, needHeight);
}
bool IcoUtils::loadIcoImageFromExe(const QString &inputFileName, QImage &image, int needWidth, int needHeight)
{
QBuffer iconData;
if (!iconData.open(QIODevice::ReadWrite)) {
return false;
}
if ( ! IcoUtils::loadIcoImageFromExe(inputFileName, &iconData) )
return false;
if (!iconData.seek(0)) {
return false;
}
return IcoUtils::loadIcoImage(&iconData, image, needWidth, needHeight);
}
bool IcoUtils::loadIcoImage(QImageReader &reader, QImage &image, int needWidth, int needHeight)
{
// QTBUG-70812: for files with incorrect bits per pixel, QImageReader::canRead() returns
// false but it can still correctly determine the imageCount() and read the icon just fine.
if (reader.imageCount() == 0) {
return false;
}
QList <QImage> icons;
do icons << reader.read();
while ( reader.jumpToNextImage() );
if ( icons.empty() )
return false;
int index = icons.size() - 1;
qreal best = std::numeric_limits<qreal>::max();
for (int i = 0; i < icons.size(); ++i) {
const QImage &icon = icons.at(i);
// QtIcoHandler converts all images to 32-bit depth,
// but it stores the actual depth of the icon extracted in custom text:
// qtbase/src/plugins/imageformats/ico/qicohandler.cpp:455
int depth = icon.text(QStringLiteral("_q_icoOrigDepth")).toInt();
if (depth == 0 || depth > 32) {
depth = icon.depth();
}
const qreal dist = distance(icon.width(), icon.height(), needWidth, needHeight, depth);
if (dist < best) {
index = i;
best = dist;
}
}
image = icons.at(index);
return true;
}
bool IcoUtils::loadIcoImage(QIODevice * inputDevice, QImage &image, int needWidth, int needHeight)
{
QImageReader reader(inputDevice, "ico");
return IcoUtils::loadIcoImage(reader, image, needWidth, needHeight);
}
bool IcoUtils::loadIcoImage(const QString &inputFileName, QImage &image, int needWidth, int needHeight)
{
QImageReader reader(inputFileName, "ico");
return IcoUtils::loadIcoImage(reader, image, needWidth, needHeight);
}
|