1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the plugins of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qsharedimageloader_p.h"
41#include <private/qobject_p.h>
42#include <private/qimage_p.h>
43#include <QSharedMemory>
44
45
46QT_BEGIN_NAMESPACE
47
48Q_LOGGING_CATEGORY(lcSharedImage, "qt.quick.sharedimage");
49
50struct SharedImageHeader {
51 quint8 magic;
52 quint8 version;
53 quint16 offset;
54 qint32 width;
55 qint32 height;
56 qint32 bpl;
57 QImage::Format format;
58};
59Q_STATIC_ASSERT(sizeof(SharedImageHeader) % 4 == 0);
60
61#if QT_CONFIG(sharedmemory)
62struct SharedImageInfo {
63 QString path;
64 QPointer<QSharedMemory> shmp;
65};
66
67void cleanupSharedImage(void *cleanupInfo)
68{
69 if (!cleanupInfo)
70 return;
71 SharedImageInfo *sii = static_cast<SharedImageInfo *>(cleanupInfo);
72 qCDebug(lcSharedImage) << "Cleanup called for" << sii->path;
73 if (sii->shmp.isNull()) {
74 qCDebug(lcSharedImage) << "shm is 0 for" << sii->path;
75 return;
76 }
77 QSharedMemory *shm = sii->shmp.data();
78 sii->shmp.clear();
79 delete shm; // destructor detaches
80 delete sii;
81}
82#else
83void cleanupSharedImage(void *) {}
84#endif
85
86class QSharedImageLoaderPrivate : public QObjectPrivate
87{
88 Q_DECLARE_PUBLIC(QSharedImageLoader)
89
90public:
91 QSharedImageLoaderPrivate() {}
92
93 QImage load(const QString &path, QSharedImageLoader::ImageParameters *params);
94
95 void storeImageToMem(void *data, const QImage &img);
96
97 bool verifyMem(const void *data, int size);
98
99 QImage createImageFromMem(const void *data, void *cleanupInfo);
100
101};
102
103
104void QSharedImageLoaderPrivate::storeImageToMem(void *data, const QImage &img)
105{
106 Q_ASSERT(data && !img.isNull());
107
108 SharedImageHeader *h = static_cast<SharedImageHeader *>(data);
109 h->magic = 'Q';
110 h->version = 1;
111 h->offset = sizeof(SharedImageHeader);
112 h->width = img.width();
113 h->height = img.height();
114 h->bpl = img.bytesPerLine();
115 h->format = img.format();
116
117 uchar *p = static_cast<uchar *>(data) + sizeof(SharedImageHeader);
118 memcpy(dest: p, src: img.constBits(), n: img.sizeInBytes());
119}
120
121
122bool QSharedImageLoaderPrivate::verifyMem(const void *data, int size)
123{
124 if (!data || size < int(sizeof(SharedImageHeader)))
125 return false;
126
127 const SharedImageHeader *h = static_cast<const SharedImageHeader *>(data);
128 if ((h->magic != 'Q')
129 || (h->version < 1)
130 || (h->offset < sizeof(SharedImageHeader))
131 || (h->width <= 0)
132 || (h->height <= 0)
133 || (h->bpl <= 0)
134 || (h->format <= QImage::Format_Invalid)
135 || (h->format >= QImage::NImageFormats)) {
136 return false;
137 }
138
139 int availSize = size - h->offset;
140 if (h->height * h->bpl > availSize)
141 return false;
142 if ((qt_depthForFormat(format: h->format) * h->width * h->height) > (8 * availSize))
143 return false;
144
145 return true;
146}
147
148
149QImage QSharedImageLoaderPrivate::createImageFromMem(const void *data, void *cleanupInfo)
150{
151 const SharedImageHeader *h = static_cast<const SharedImageHeader *>(data);
152 const uchar *p = static_cast<const uchar *>(data) + h->offset;
153
154 QImage img(p, h->width, h->height, h->bpl, h->format, cleanupSharedImage, cleanupInfo);
155 return img;
156}
157
158
159QImage QSharedImageLoaderPrivate::load(const QString &path, QSharedImageLoader::ImageParameters *params)
160{
161#if QT_CONFIG(sharedmemory)
162 Q_Q(QSharedImageLoader);
163
164 QImage nil;
165 if (path.isEmpty())
166 return nil;
167
168 QScopedPointer<QSharedMemory> shm(new QSharedMemory(q->key(path, params)));
169 bool locked = false;
170
171 if (!shm->attach(mode: QSharedMemory::ReadOnly)) {
172 QImage img = q->loadFile(path, params);
173 if (img.isNull())
174 return nil;
175 size_t size = sizeof(SharedImageHeader) + img.sizeInBytes();
176 if (size > size_t(std::numeric_limits<int>::max())) {
177 qCDebug(lcSharedImage) << "Image" << path << "to large to load";
178 return nil;
179 } else if (shm->create(size: int(size))) {
180 qCDebug(lcSharedImage) << "Created new shm segment of size" << size << "for image" << path;
181 if (!shm->lock()) {
182 qCDebug(lcSharedImage) << "Lock1 failed!?" << shm->errorString();
183 return nil;
184 }
185 locked = true;
186 storeImageToMem(data: shm->data(), img);
187 } else if (shm->error() == QSharedMemory::AlreadyExists) {
188 // race handling: other process may have created the share while
189 // we loaded the image, so try again to just attach
190 if (!shm->attach(mode: QSharedMemory::ReadOnly)) {
191 qCDebug(lcSharedImage) << "Attach to existing failed?" << shm->errorString();
192 return nil;
193 }
194 } else {
195 qCDebug(lcSharedImage) << "Create failed?" << shm->errorString();
196 return nil;
197 }
198 }
199
200 Q_ASSERT(shm->isAttached());
201
202 if (!locked) {
203 if (!shm->lock()) {
204 qCDebug(lcSharedImage) << "Lock2 failed!?" << shm->errorString();
205 return nil;
206 }
207 locked = true;
208 }
209
210 if (!verifyMem(data: shm->constData(), size: shm->size())) {
211 qCDebug(lcSharedImage) << "Verifymem failed!?";
212 shm->unlock();
213 return nil;
214 }
215
216 QSharedMemory *shmp = shm.take();
217 SharedImageInfo *sii = new SharedImageInfo;
218 sii->path = path;
219 sii->shmp = shmp;
220 QImage shImg = createImageFromMem(data: shmp->constData(), cleanupInfo: sii);
221
222 if (!shmp->unlock()) {
223 qCDebug(lcSharedImage) << "UnLock failed!?";
224 }
225
226 return shImg;
227#else
228 Q_UNUSED(path);
229 Q_UNUSED(params);
230 return QImage();
231#endif
232}
233
234
235QSharedImageLoader::QSharedImageLoader(QObject *parent)
236 : QObject(*new QSharedImageLoaderPrivate, parent)
237{
238}
239
240QSharedImageLoader::~QSharedImageLoader()
241{
242}
243
244QImage QSharedImageLoader::load(const QString &path, ImageParameters *params)
245{
246 Q_D(QSharedImageLoader);
247
248 return d->load(path, params);
249}
250
251QImage QSharedImageLoader::loadFile(const QString &path, ImageParameters *params)
252{
253 Q_UNUSED(params);
254
255 return QImage(path);
256}
257
258QString QSharedImageLoader::key(const QString &path, ImageParameters *params)
259{
260 Q_UNUSED(params);
261
262 return path;
263}
264
265
266QT_END_NAMESPACE
267

source code of qtdeclarative/src/imports/sharedimage/qsharedimageloader.cpp