Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qiiofhelpers.mm
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
5#include <QGuiApplication>
6#include <QBuffer>
7#include <QImageIOHandler>
8#include <QImage>
9
10#include <QtCore/qloggingcategory.h>
11
12#include <QtGui/private/qcoregraphics_p.h>
13
14#include "qiiofhelpers_p.h"
15
16
18
19Q_STATIC_LOGGING_CATEGORY(lcImageIO, "qt.imageformats.imageio", QtCriticalMsg)
20
21namespace NS_IIOF_HELPERS {
22
23// Callbacks for sequential data provider & consumer:
24
25static size_t cbGetBytes(void *info, void *buffer, size_t count)
26{
27 QIODevice *dev = static_cast<QIODevice *>(info);
28 qCDebug(lcImageIO) << "Reading" << count << "bytes from" << dev << "into" << buffer;
29
30 if (!dev || !buffer)
31 return 0;
32 qint64 res = dev->read(static_cast<char *>(buffer), qint64(count));
33 return size_t(qMax(qint64(0), res));
34}
35
36static off_t cbSkipForward(void *info, off_t count)
37{
38 QIODevice *dev = static_cast<QIODevice *>(info);
39 qCDebug(lcImageIO) << "Skipping" << dev << "forward" << count << "bytes" ;
40
41 if (!dev || count <= 0)
42 return 0;
43 qint64 res = 0;
44 if (!dev->isSequential()) {
45 qint64 prevPos = dev->pos();
46 dev->seek(prevPos + count);
47 res = dev->pos() - prevPos;
48 } else {
49 char *buf = new char[quint64(count)];
50 res = dev->read(buf, count);
51 delete[] buf;
52 }
53 return qMax(qint64(0), res);
54}
55
56static void cbRewind(void *info)
57{
58 QIODevice *dev = static_cast<QIODevice *>(info);
59 qCDebug(lcImageIO) << "Rewinding" << dev;
60 // Ignore this; we do not want the Qt device to be rewound after reading the image
61}
62
63static size_t cbPutBytes(void *info, const void *buffer, size_t count)
64{
65 QIODevice *dev = static_cast<QIODevice *>(info);
66 qCDebug(lcImageIO) << "Writing" << count << "bytes from" << buffer << "into" << dev;
67
68 if (!dev || !buffer)
69 return 0;
70 qint64 res = dev->write(static_cast<const char *>(buffer), qint64(count));
71 return size_t(qMax(qint64(0), res));
72}
73
74static void cbRelease(void *info)
75{
76 qCDebug(lcImageIO) << "Releasing" << info;
77}
78
79QImageIOPlugin::Capabilities QIIOFHelpers::systemCapabilities(const QString &uti)
80{
81 QImageIOPlugin::Capabilities res;
82 QCFString cfUti(uti);
83
84 QCFType<CFArrayRef> cfSourceTypes = CGImageSourceCopyTypeIdentifiers();
85 CFIndex len = CFArrayGetCount(cfSourceTypes);
86 if (CFArrayContainsValue(cfSourceTypes, CFRangeMake(0, len), cfUti))
87 res |= QImageIOPlugin::CanRead;
88
89 QCFType<CFArrayRef> cfDestTypes = CGImageDestinationCopyTypeIdentifiers();
90 len = CFArrayGetCount(cfDestTypes);
91 if (CFArrayContainsValue(cfDestTypes, CFRangeMake(0, len), cfUti))
92 res |= QImageIOPlugin::CanWrite;
93
94 return res;
95}
96
97bool QIIOFHelpers::readImage(QImageIOHandler *q_ptr, QImage *out)
98{
99 QIIOFHelper h(q_ptr);
100 return h.readImage(out);
101}
102
103bool QIIOFHelpers::writeImage(QImageIOHandler *q_ptr, const QImage &in, const QString &uti)
104{
105 QIIOFHelper h(q_ptr);
106 return h.writeImage(in, uti);
107}
108
109QIIOFHelper::QIIOFHelper(QImageIOHandler *q)
110 : q_ptr(q)
111{
112}
113
115{
116 if (cgImageSource)
117 return true;
118 if (!q_ptr || !q_ptr->device())
119 return false;
120
121 if (QBuffer *b = qobject_cast<QBuffer *>(q_ptr->device())) {
122 // do direct access to avoid data copy
123 const void *rawData = b->data().constData() + b->pos();
124 cgDataProvider = CGDataProviderCreateWithData(nullptr, rawData, size_t(b->data().size() - b->pos()), nullptr);
125 } else {
126 static const CGDataProviderSequentialCallbacks cgCallbacks = {
127 0, // Version, always 0
128 &cbGetBytes, &cbSkipForward, &cbRewind, &cbRelease
129 };
130 cgDataProvider = CGDataProviderCreateSequential(q_ptr->device(), &cgCallbacks);
131 }
132
133 cgImageSource = CGImageSourceCreateWithDataProvider(cgDataProvider, nullptr);
134
135 if (cgImageSource) {
136 auto primaryIndex = CGImageSourceGetPrimaryImageIndex(cgImageSource);
137 cfImageDict = CGImageSourceCopyPropertiesAtIndex(cgImageSource, primaryIndex, nullptr);
138 }
139
140 qCInfo(lcImageIO) << "Initialized source" << cgImageSource
141 << "with" << CGImageSourceGetCount(cgImageSource) << "images"
142 << "and primary index" << CGImageSourceGetPrimaryImageIndex(cgImageSource);
143
144 return (cgImageSource);
145}
146
147bool QIIOFHelper::readImage(QImage *out)
148{
149 if (!out || !initRead())
150 return false;
151
152 auto primaryIndex = CGImageSourceGetPrimaryImageIndex(cgImageSource);
153 QCFType<CGImageRef> cgImage = CGImageSourceCreateImageAtIndex(cgImageSource, primaryIndex, nullptr);
154
155 qCInfo(lcImageIO) << "Read image at index" << primaryIndex
156 << "with properties" << cfImageDict << "and got" << cgImage;
157
158 if (!cgImage)
159 return false;
160
161 *out = qt_mac_toQImage(cgImage);
162 if (out->isNull())
163 return false;
164
165 int dpi = 0;
166 if (getIntProperty(kCGImagePropertyDPIWidth, &dpi))
167 out->setDotsPerMeterX(qRound(dpi / 0.0254f));
168 if (getIntProperty(kCGImagePropertyDPIHeight, &dpi))
169 out->setDotsPerMeterY(qRound(dpi / 0.0254f));
170
171 return true;
172}
173
174bool QIIOFHelper::getIntProperty(CFStringRef property, int *value)
175{
176 if (!cfImageDict)
177 return false;
178
179 CFNumberRef cfNumber = static_cast<CFNumberRef>(CFDictionaryGetValue(cfImageDict, property));
180 if (cfNumber) {
181 int intVal;
182 if (CFNumberGetValue(cfNumber, kCFNumberIntType, &intVal)) {
183 if (value)
184 *value = intVal;
185 return true;
186 }
187 }
188 return false;
189}
190
191static QImageIOHandler::Transformations exif2Qt(int exifOrientation)
192{
193 switch (exifOrientation) {
194 case 1: // normal
195 return QImageIOHandler::TransformationNone;
196 case 2: // mirror horizontal
197 return QImageIOHandler::TransformationMirror;
198 case 3: // rotate 180
199 return QImageIOHandler::TransformationRotate180;
200 case 4: // mirror vertical
201 return QImageIOHandler::TransformationFlip;
202 case 5: // mirror horizontal and rotate 270 CW
203 return QImageIOHandler::TransformationFlipAndRotate90;
204 case 6: // rotate 90 CW
205 return QImageIOHandler::TransformationRotate90;
206 case 7: // mirror horizontal and rotate 90 CW
207 return QImageIOHandler::TransformationMirrorAndRotate90;
208 case 8: // rotate 270 CW
209 return QImageIOHandler::TransformationRotate270;
210 }
211 return QImageIOHandler::TransformationNone;
212}
213
214static int qt2Exif(QImageIOHandler::Transformations transformation)
215{
216 switch (transformation) {
217 case QImageIOHandler::TransformationNone:
218 return 1;
219 case QImageIOHandler::TransformationMirror:
220 return 2;
221 case QImageIOHandler::TransformationRotate180:
222 return 3;
223 case QImageIOHandler::TransformationFlip:
224 return 4;
225 case QImageIOHandler::TransformationFlipAndRotate90:
226 return 5;
227 case QImageIOHandler::TransformationRotate90:
228 return 6;
229 case QImageIOHandler::TransformationMirrorAndRotate90:
230 return 7;
231 case QImageIOHandler::TransformationRotate270:
232 return 8;
233 }
234 qWarning("Invalid Qt image transformation");
235 return 1;
236}
237
238QVariant QIIOFHelper::imageProperty(QImageIOHandler::ImageOption option)
239{
240 if (!initRead())
241 return QVariant();
242
243 switch (option) {
244 case QImageIOHandler::Size: {
245 QSize sz;
246 if (getIntProperty(kCGImagePropertyPixelWidth, &sz.rwidth())
247 && getIntProperty(kCGImagePropertyPixelHeight, &sz.rheight())) {
248 return sz;
249 }
250 break;
251 }
252 case QImageIOHandler::ImageTransformation: {
253 int orient;
254 if (getIntProperty(kCGImagePropertyOrientation, &orient))
255 return int(exif2Qt(orient));
256 break;
257 }
258 default:
259 break;
260 }
261
262 return QVariant();
263}
264
265void QIIOFHelper::setOption(QImageIOHandler::ImageOption option, const QVariant &value)
266{
267 if (writeOptions.size() < option + 1)
268 writeOptions.resize(option + 1);
269 writeOptions[option] = value;
270}
271
272bool QIIOFHelper::writeImage(const QImage &in, const QString &uti)
273{
274 static const CGDataConsumerCallbacks cgCallbacks = { &cbPutBytes, nullptr };
275
276 if (!q_ptr || !q_ptr->device() || in.isNull())
277 return false;
278
279 QCFType<CGImageRef> cgImage = qt_mac_toCGImage(in);
280 QCFType<CGDataConsumerRef> cgDataConsumer = CGDataConsumerCreate(q_ptr->device(), &cgCallbacks);
281 QCFType<CFStringRef> cfUti = uti.toCFString();
282 QCFType<CGImageDestinationRef> cgImageDest = CGImageDestinationCreateWithDataConsumer(cgDataConsumer, cfUti, 1, nullptr);
283 if (!cgImageDest || !cgImage)
284 return false;
285
286 QCFType<CFNumberRef> cfQuality = nullptr;
287 QCFType<CFNumberRef> cfOrientation = nullptr;
288 const void *dictKeys[2];
289 const void *dictVals[2];
290 int dictSize = 0;
291
292 if (q_ptr->supportsOption(QImageIOHandler::Quality)) {
293 bool ok = false;
294 int writeQuality = writeOptions.value(QImageIOHandler::Quality).toInt(&ok);
295 // If quality is unset, default to 75%
296 float quality = (ok && writeQuality >= 0 ? (qMin(writeQuality, 100)) : 75) / 100.0f;
297 cfQuality = CFNumberCreate(nullptr, kCFNumberFloatType, &quality);
298 dictKeys[dictSize] = static_cast<const void *>(kCGImageDestinationLossyCompressionQuality);
299 dictVals[dictSize] = static_cast<const void *>(cfQuality);
300 dictSize++;
301 }
302 if (q_ptr->supportsOption(QImageIOHandler::ImageTransformation)) {
303 int orient = qt2Exif(static_cast<QImageIOHandler::Transformation>(writeOptions.value(QImageIOHandler::ImageTransformation).toInt()));
304 cfOrientation = CFNumberCreate(nullptr, kCFNumberIntType, &orient);
305 dictKeys[dictSize] = static_cast<const void *>(kCGImagePropertyOrientation);
306 dictVals[dictSize] = static_cast<const void *>(cfOrientation);
307 dictSize++;
308 }
309
310 QCFType<CFDictionaryRef> cfProps = nullptr;
311 if (dictSize)
312 cfProps = CFDictionaryCreate(nullptr, dictKeys, dictVals, dictSize,
313 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
314
315 qCInfo(lcImageIO) << "Writing" << cgImage << "\nwith properties" << cfProps;
316
317 CGImageDestinationAddImage(cgImageDest, cgImage, cfProps);
318 return CGImageDestinationFinalize(cgImageDest);
319}
320
321}
322
323QT_END_NAMESPACE
QIIOFHelper(QImageIOHandler *q)
bool getIntProperty(CFStringRef property, int *value)
QVariant imageProperty(QImageIOHandler::ImageOption option)
static void cbRelease(void *info)
static size_t cbPutBytes(void *info, const void *buffer, size_t count)
static size_t cbGetBytes(void *info, void *buffer, size_t count)
static int qt2Exif(QImageIOHandler::Transformations transformation)
static off_t cbSkipForward(void *info, off_t count)
static QImageIOHandler::Transformations exif2Qt(int exifOrientation)
static void cbRewind(void *info)
Combined button and popup list for selecting options.