/**************************************************************************** ** ** 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 #include #include 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 shmp; }; void cleanupSharedImage(void *cleanupInfo) { if (!cleanupInfo) return; SharedImageInfo *sii = static_cast(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(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(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(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(data); const uchar *p = static_cast(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 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 > std::numeric_limits::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