1/*
2 SPDX-FileCopyrightText: 2022 Albert Astals Cid <aacid@kde.org>
3 SPDX-FileCopyrightText: 2022 Mirco Miranda <mircomir@outlook.com>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#ifndef UTIL_P_H
9#define UTIL_P_H
10
11#include <limits>
12
13#include <QImage>
14#include <QImageIOHandler>
15#include <QIODevice>
16
17// Default maximum width and height for the large image plugins.
18#ifndef KIF_LARGE_IMAGE_PIXEL_LIMIT
19#define KIF_LARGE_IMAGE_PIXEL_LIMIT 300000
20#endif
21
22// Image metadata keys to use in plugins (so they are consistent)
23#define META_KEY_ALTITUDE "Altitude"
24#define META_KEY_AUTHOR "Author"
25#define META_KEY_COMMENT "Comment"
26#define META_KEY_COPYRIGHT "Copyright"
27#define META_KEY_CREATIONDATE "CreationDate"
28#define META_KEY_DESCRIPTION "Description"
29#define META_KEY_DIRECTION "Direction"
30#define META_KEY_DOCUMENTNAME "DocumentName"
31#define META_KEY_HOSTCOMPUTER "HostComputer"
32#define META_KEY_LATITUDE "Latitude"
33#define META_KEY_LONGITUDE "Longitude"
34#define META_KEY_MODIFICATIONDATE "ModificationDate"
35#define META_KEY_OWNER "Owner"
36#define META_KEY_SOFTWARE "Software"
37#define META_KEY_TITLE "Title"
38#define META_KEY_XML_GIMP "XML:org.gimp.xml"
39#define META_KEY_XMP_ADOBE "XML:com.adobe.xmp"
40
41// Camera info metadata keys
42#define META_KEY_MANUFACTURER "Manufacturer"
43#define META_KEY_MODEL "Model"
44#define META_KEY_SERIALNUMBER "SerialNumber"
45
46// Lens info metadata keys
47#define META_KEY_LENS_MANUFACTURER "LensManufacturer"
48#define META_KEY_LENS_MODEL "LensModel"
49#define META_KEY_LENS_SERIALNUMBER "LensSerialNumber"
50
51// QList uses some extra space for stuff, hence the 32 here suggested by Thiago Macieira
52static constexpr int kMaxQVectorSize = std::numeric_limits<int>::max() - 32;
53
54// On Qt 6 to make the plugins fail to allocate if the image size is greater than QImageReader::allocationLimit()
55// it is necessary to allocate the image with QImageIOHandler::allocateImage().
56inline QImage imageAlloc(const QSize &size, const QImage::Format &format)
57{
58 QImage img;
59 if (!QImageIOHandler::allocateImage(size, format, image: &img)) {
60 img = QImage(); // paranoia
61 }
62 return img;
63}
64
65inline QImage imageAlloc(qint32 width, qint32 height, const QImage::Format &format)
66{
67 return imageAlloc(size: QSize(width, height), format);
68}
69
70template<class TI, class SF> // SF = source FP, TI = target INT
71TI qRoundOrZero_T(SF d, bool *ok = nullptr)
72{
73 // checks for undefined behavior
74 if (qIsNaN(d) || qIsInf(d) || d < SF() || d > SF(std::numeric_limits<TI>::max())) {
75 if (ok) {
76 *ok = false;
77 }
78 return 0;
79 }
80 if (ok) {
81 *ok = true;
82 }
83 return qRound(d);
84}
85
86inline qint32 qRoundOrZero(double d, bool *ok = nullptr)
87{
88 return qRoundOrZero_T<qint32>(d, ok);
89}
90inline qint32 qRoundOrZero(float d, bool *ok = nullptr)
91{
92 return qRoundOrZero_T<qint32>(d, ok);
93}
94
95/*!
96 * \brief dpi2ppm
97 * Converts a value from DPI to PPM.
98 * \return \a dpi converted to pixel per meter.
99 */
100inline qint32 dpi2ppm(double dpi, bool *ok = nullptr)
101{
102 return qRoundOrZero(d: dpi / double(25.4) * double(1000), ok);
103}
104inline qint32 dpi2ppm(float dpi, bool *ok = nullptr)
105{
106 return qRoundOrZero(d: dpi / float(25.4) * float(1000), ok);
107}
108inline qint32 dpi2ppm(quint16 dpi, bool *ok = nullptr)
109{
110 return qRoundOrZero(d: dpi / double(25.4) * double(1000), ok);
111}
112
113/*!
114 * \brief ppm2dpi
115 * Converts a value from PPM to DPI.
116 * \return \a ppm converted to dot per inch.
117 */
118template<class TF, class SI> // SI = source INT, TF = target FP
119TF ppm2dpi_T(SI ppm, bool *ok = nullptr)
120{
121 if (ok) {
122 *ok = ppm > 0;
123 }
124 return ppm > 0 ? ppm * TF(25.4) / TF(1000) : TF();
125}
126
127inline double dppm2dpi(qint32 ppm, bool *ok = nullptr)
128{
129 return ppm2dpi_T<double>(ppm, ok);
130}
131inline float fppm2dpi(qint32 ppm, bool *ok = nullptr)
132{
133 return ppm2dpi_T<float>(ppm, ok);
134}
135
136/*!
137 * \brief deviceRead
138 * A function for reading from devices.
139 *
140 * Similar to QIODevice::read(qint64) but limits the initial memory allocation. Useful for reading corrupted streams.
141 * \param d The device.
142 * \param maxSize The maximum size to read.
143 * \return The byte array read.
144 */
145static QByteArray deviceRead(QIODevice *d, qint64 maxSize)
146{
147 if (d == nullptr) {
148 return{};
149 }
150
151 const qint64 blockSize = 32 * 1024 * 1024;
152 auto devSize = d->isSequential() ? qint64() : d->size();
153
154 if (devSize > 0) {
155 // random access device
156 maxSize = std::min(a: maxSize, b: devSize - d->pos());
157 return d->read(maxlen: maxSize);
158 } else if (maxSize < blockSize) {
159 // small read
160 return d->read(maxlen: maxSize);
161 }
162
163 // sequential device
164 QByteArray ba;
165 while (ba.size() < maxSize) {
166 auto toRead = std::min(a: blockSize, b: maxSize - ba.size());
167 if (toRead + ba.size() > QByteArray::maxSize()) {
168 break;
169 }
170 auto tmp = d->read(maxlen: toRead);
171 if (tmp.isEmpty()) {
172 break;
173 }
174 ba.append(a: tmp);
175 }
176
177 return ba;
178}
179
180#endif // UTIL_P_H
181

source code of kimageformats/src/imageformats/util_p.h