File: icoutils_common.cpp

package info (click to toggle)
kio-extras 4%3A22.12.3-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 10,768 kB
  • sloc: cpp: 26,898; ansic: 4,443; perl: 1,058; xml: 97; sh: 69; python: 28; makefile: 9
file content (131 lines) | stat: -rw-r--r-- 3,839 bytes parent folder | download
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);

}