diff options
Diffstat (limited to 'src/labs/sharedimage/qsharedimageloader.cpp')
-rw-r--r-- | src/labs/sharedimage/qsharedimageloader.cpp | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/src/labs/sharedimage/qsharedimageloader.cpp b/src/labs/sharedimage/qsharedimageloader.cpp new file mode 100644 index 0000000000..c9e3ef3eb3 --- /dev/null +++ b/src/labs/sharedimage/qsharedimageloader.cpp @@ -0,0 +1,266 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsharedimageloader_p.h" +#include <private/qobject_p.h> +#include <private/qimage_p.h> +#include <QSharedMemory> + + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcSharedImage, "qt.quick.sharedimage"); + +struct SharedImageHeader { + quint8 magic; + quint8 version; + quint16 offset; + qint32 width; + qint32 height; + qint32 bpl; + QImage::Format format; +}; +Q_STATIC_ASSERT(sizeof(SharedImageHeader) % 4 == 0); + +#if QT_CONFIG(sharedmemory) +struct SharedImageInfo { + QString path; + QPointer<QSharedMemory> shmp; +}; + +void cleanupSharedImage(void *cleanupInfo) +{ + if (!cleanupInfo) + return; + SharedImageInfo *sii = static_cast<SharedImageInfo *>(cleanupInfo); + qCDebug(lcSharedImage) << "Cleanup called for" << sii->path; + if (sii->shmp.isNull()) { + qCDebug(lcSharedImage) << "shm is 0 for" << sii->path; + return; + } + QSharedMemory *shm = sii->shmp.data(); + sii->shmp.clear(); + delete shm; // destructor detaches + delete sii; +} +#else +void cleanupSharedImage(void *) {} +#endif + +class QSharedImageLoaderPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QSharedImageLoader) + +public: + QSharedImageLoaderPrivate() {} + + QImage load(const QString &path, QSharedImageLoader::ImageParameters *params); + + void storeImageToMem(void *data, const QImage &img); + + bool verifyMem(const void *data, int size); + + QImage createImageFromMem(const void *data, void *cleanupInfo); + +}; + + +void QSharedImageLoaderPrivate::storeImageToMem(void *data, const QImage &img) +{ + Q_ASSERT(data && !img.isNull()); + + SharedImageHeader *h = static_cast<SharedImageHeader *>(data); + h->magic = 'Q'; + h->version = 1; + h->offset = sizeof(SharedImageHeader); + h->width = img.width(); + h->height = img.height(); + h->bpl = img.bytesPerLine(); + h->format = img.format(); + + uchar *p = static_cast<uchar *>(data) + sizeof(SharedImageHeader); + memcpy(p, img.constBits(), img.sizeInBytes()); +} + + +bool QSharedImageLoaderPrivate::verifyMem(const void *data, int size) +{ + if (!data || size < int(sizeof(SharedImageHeader))) + return false; + + const SharedImageHeader *h = static_cast<const SharedImageHeader *>(data); + if ((h->magic != 'Q') + || (h->version < 1) + || (h->offset < sizeof(SharedImageHeader)) + || (h->width <= 0) + || (h->height <= 0) + || (h->bpl <= 0) + || (h->format <= QImage::Format_Invalid) + || (h->format >= QImage::NImageFormats)) { + return false; + } + + int availSize = size - h->offset; + if (h->height * h->bpl > availSize) + return false; + if ((qt_depthForFormat(h->format) * h->width * h->height) > (8 * availSize)) + return false; + + return true; +} + + +QImage QSharedImageLoaderPrivate::createImageFromMem(const void *data, void *cleanupInfo) +{ + const SharedImageHeader *h = static_cast<const SharedImageHeader *>(data); + const uchar *p = static_cast<const uchar *>(data) + h->offset; + + QImage img(p, h->width, h->height, h->bpl, h->format, cleanupSharedImage, cleanupInfo); + return img; +} + + +QImage QSharedImageLoaderPrivate::load(const QString &path, QSharedImageLoader::ImageParameters *params) +{ +#if QT_CONFIG(sharedmemory) + Q_Q(QSharedImageLoader); + + QImage nil; + if (path.isEmpty()) + return nil; + + QScopedPointer<QSharedMemory> shm(new QSharedMemory(q->key(path, params))); + bool locked = false; + + if (!shm->attach(QSharedMemory::ReadOnly)) { + QImage img = q->loadFile(path, params); + if (img.isNull()) + return nil; + size_t size = sizeof(SharedImageHeader) + img.sizeInBytes(); + if (size > size_t(std::numeric_limits<int>::max())) { + qCDebug(lcSharedImage) << "Image" << path << "to large to load"; + return nil; + } else if (shm->create(int(size))) { + qCDebug(lcSharedImage) << "Created new shm segment of size" << size << "for image" << path; + if (!shm->lock()) { + qCDebug(lcSharedImage) << "Lock1 failed!?" << shm->errorString(); + return nil; + } + locked = true; + storeImageToMem(shm->data(), img); + } else if (shm->error() == QSharedMemory::AlreadyExists) { + // race handling: other process may have created the share while + // we loaded the image, so try again to just attach + if (!shm->attach(QSharedMemory::ReadOnly)) { + qCDebug(lcSharedImage) << "Attach to existing failed?" << shm->errorString(); + return nil; + } + } else { + qCDebug(lcSharedImage) << "Create failed?" << shm->errorString(); + return nil; + } + } + + Q_ASSERT(shm->isAttached()); + + if (!locked) { + if (!shm->lock()) { + qCDebug(lcSharedImage) << "Lock2 failed!?" << shm->errorString(); + return nil; + } + locked = true; + } + + if (!verifyMem(shm->constData(), shm->size())) { + qCDebug(lcSharedImage) << "Verifymem failed!?"; + shm->unlock(); + return nil; + } + + QSharedMemory *shmp = shm.take(); + SharedImageInfo *sii = new SharedImageInfo; + sii->path = path; + sii->shmp = shmp; + QImage shImg = createImageFromMem(shmp->constData(), sii); + + if (!shmp->unlock()) { + qCDebug(lcSharedImage) << "UnLock failed!?"; + } + + return shImg; +#else + Q_UNUSED(path); + Q_UNUSED(params); + return QImage(); +#endif +} + + +QSharedImageLoader::QSharedImageLoader(QObject *parent) + : QObject(*new QSharedImageLoaderPrivate, parent) +{ +} + +QSharedImageLoader::~QSharedImageLoader() +{ +} + +QImage QSharedImageLoader::load(const QString &path, ImageParameters *params) +{ + Q_D(QSharedImageLoader); + + return d->load(path, params); +} + +QImage QSharedImageLoader::loadFile(const QString &path, ImageParameters *params) +{ + Q_UNUSED(params); + + return QImage(path); +} + +QString QSharedImageLoader::key(const QString &path, ImageParameters *params) +{ + Q_UNUSED(params); + + return path; +} + + +QT_END_NAMESPACE |