From fcf8d2bde4dbb0bcfa5f903f4822b750fde5f819 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 13 Feb 2025 17:07:09 +0200 Subject: [PATCH] QmlDesigner: Change ImageCache to use new qml-renderer puppet ImageCacheCollector now uses new qml-renderer puppet to generate preview images. Removed -W and -H args from qml-renderer and added --minW, --minH, --maxW and --maxH args to specify acceptable range for final scaled size of the image. If natural size of the rendered image fits within this range, then the image is not scaled. Added --libIcon arg to qml-renderer to suppress background on 3D nodes. Also changed item library icons to respect aspect ratio. Fixes: QDS-14692 Change-Id: I88f3461626f69191770c0c5e7d79c14d7e82e609 Reviewed-by: Mahmoud Badri Reviewed-by: Marco Bubke --- .../itemLibraryQmlSources/ItemDelegate.qml | 1 + .../commands/createscenecommand.h | 10 - src/plugins/qmldesigner/CMakeLists.txt | 2 - .../assetslibrary/assetslibraryview.cpp | 1 - .../itemlibraryiconimageprovider.h | 1 - .../imagecachecollector.cpp | 192 ++++++++---------- .../imagecachecollector.h | 25 +-- .../imagecacheconnectionmanager.cpp | 56 ----- .../imagecacheconnectionmanager.h | 31 --- .../meshimagecachecollector.cpp | 7 +- .../meshimagecachecollector.h | 5 +- .../instances/nodeinstanceview.cpp | 2 - .../qmldesigner/instances/nodeinstanceview.h | 9 - .../qmldesigner/qmldesignerprojectmanager.cpp | 14 +- src/tools/qmlpuppet/CMakeLists.txt | 1 - .../qmlpuppet/instances/instances.pri | 2 - .../nodeinstanceserverdispatcher.cpp | 3 - .../qt5captureimagenodeinstanceserver.cpp | 79 ------- .../qt5captureimagenodeinstanceserver.h | 27 --- .../instances/qt5nodeinstanceclientproxy.cpp | 4 - .../qmlpuppet/renderer/qmlrenderer.cpp | 117 ++++++++--- .../qmlpuppet/renderer/qmlrenderer.h | 6 +- 22 files changed, 193 insertions(+), 402 deletions(-) delete mode 100644 src/plugins/qmldesigner/imagecachecollectors/imagecacheconnectionmanager.cpp delete mode 100644 src/plugins/qmldesigner/imagecachecollectors/imagecacheconnectionmanager.h delete mode 100644 src/tools/qmlpuppet/qmlpuppet/instances/qt5captureimagenodeinstanceserver.cpp delete mode 100644 src/tools/qmlpuppet/qmlpuppet/instances/qt5captureimagenodeinstanceserver.h diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml index 0e3099361a9..449bf3e6905 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml @@ -38,6 +38,7 @@ Item { width: ItemLibraryBackend.itemLibraryIconWidth // to be set in Qml context height: ItemLibraryBackend.itemLibraryIconHeight // to be set in Qml context source: itemLibraryIconPath // to be set by model + fillMode: Image.PreserveAspectFit // Icons generated for components can change if the component is edited, // so don't cache them locally at Image level. diff --git a/src/libs/qmlpuppetcommunication/commands/createscenecommand.h b/src/libs/qmlpuppetcommunication/commands/createscenecommand.h index f052da1eaaa..80456169db5 100644 --- a/src/libs/qmlpuppetcommunication/commands/createscenecommand.h +++ b/src/libs/qmlpuppetcommunication/commands/createscenecommand.h @@ -36,8 +36,6 @@ public: const QUrl &resourceUrl, const QHash &edit3dToolStates, const QString &language, - QSize captureImageMinimumSize, - QSize captureImageMaximumSize, qint32 stateInstanceId) : instances(instanceContainer) , reparentInstances(reparentContainer) @@ -51,8 +49,6 @@ public: , resourceUrl(resourceUrl) , edit3dToolStates(edit3dToolStates) , language(language) - , captureImageMinimumSize(captureImageMinimumSize) - , captureImageMaximumSize(captureImageMaximumSize) , stateInstanceId{stateInstanceId} {} @@ -71,8 +67,6 @@ public: out << command.edit3dToolStates; out << command.language; out << command.stateInstanceId; - out << command.captureImageMinimumSize; - out << command.captureImageMaximumSize; return out; } @@ -92,8 +86,6 @@ public: in >> command.edit3dToolStates; in >> command.language; in >> command.stateInstanceId; - in >> command.captureImageMinimumSize; - in >> command.captureImageMaximumSize; return in; } @@ -111,8 +103,6 @@ public: QUrl resourceUrl; QHash edit3dToolStates; QString language; - QSize captureImageMinimumSize; - QSize captureImageMaximumSize; qint32 stateInstanceId = 0; }; diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 5e5e7ca998d..0ba34894e2a 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -517,8 +517,6 @@ extend_qtc_plugin(QmlDesigner SOURCES imagecachecollector.cpp imagecachecollector.h - imagecacheconnectionmanager.cpp - imagecacheconnectionmanager.h imagecachefontcollector.cpp imagecachefontcollector.h meshimagecachecollector.cpp diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.cpp index c3c8ed5e33a..db90d66a275 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.h index 87d97d65d43..a7c3a66fb4a 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.h @@ -12,7 +12,6 @@ #include #include #include -#include #include diff --git a/src/plugins/qmldesigner/imagecachecollectors/imagecachecollector.cpp b/src/plugins/qmldesigner/imagecachecollectors/imagecachecollector.cpp index ae275b85041..aff2b792f2f 100644 --- a/src/plugins/qmldesigner/imagecachecollectors/imagecachecollector.cpp +++ b/src/plugins/qmldesigner/imagecachecollectors/imagecachecollector.cpp @@ -2,50 +2,31 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "imagecachecollector.h" -#include "imagecacheconnectionmanager.h" -#include -#include -#include -#include -#include +#include +#include +#include #include #include #include +#include #include -#include +#include +#include namespace QmlDesigner { namespace { -QByteArray fileToByteArray(const QString &filename) -{ - QFile file(filename); - QFileInfo fleInfo(file); - - if (fleInfo.exists() && file.open(QFile::ReadOnly)) - return file.readAll(); - - return {}; -} - -QString fileToString(const QString &filename) -{ - return QString::fromUtf8(fileToByteArray(filename)); -} - } // namespace -ImageCacheCollector::ImageCacheCollector(ImageCacheConnectionManager &connectionManager, - QSize captureImageMinimumSize, +ImageCacheCollector::ImageCacheCollector(QSize captureImageMinimumSize, QSize captureImageMaximumSize, ExternalDependenciesInterface &externalDependencies, ImageCacheCollectorNullImageHandling nullImageHandling) - : m_connectionManager{connectionManager} - , captureImageMinimumSize{captureImageMinimumSize} + : captureImageMinimumSize{captureImageMinimumSize} , captureImageMaximumSize{captureImageMaximumSize} , m_externalDependencies{externalDependencies} , nullImageHandling{nullImageHandling} @@ -65,108 +46,41 @@ QImage scaleImage(const QImage &image, QSize targetSize) QSize scaledImageSize = image.size().scaled(targetSize.boundedTo(image.size()), Qt::KeepAspectRatio); return image.scaled(scaledImageSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - } } // namespace void ImageCacheCollector::start(Utils::SmallStringView name, - Utils::SmallStringView state, + Utils::SmallStringView, const ImageCache::AuxiliaryData &auxiliaryData, CaptureCallback captureCallback, AbortCallback abortCallback, ImageCache::TraceToken traceToken) { -#ifdef QDS_USE_PROJECTSTORAGE - if (!m_projectStorage || !m_pathCache) - return; -#endif - using namespace NanotraceHR::Literals; auto [collectorTraceToken, flowtoken] = traceToken.beginDurationWithFlow( "generate image in standard collector"); - RewriterView rewriterView{m_externalDependencies, RewriterView::Amend}; - rewriterView.setPossibleImportsEnabled(false); - NodeInstanceView nodeInstanceView{m_connectionManager, m_externalDependencies}; - nodeInstanceView.setCaptureImageMinimumAndMaximumSize(captureImageMinimumSize, - captureImageMaximumSize); + QTemporaryDir outDir(QDir::tempPath() + "/qds_imagecache_XXXXXX"); + QString outFile = outDir.filePath("capture.png"); - const QString filePath{name}; -#ifdef QDS_USE_PROJECTSTORAGE - auto model = QmlDesigner::Model::create({*m_projectStorage, *m_pathCache}, - "Item", - {}, - QUrl::fromLocalFile(filePath)); -#else - auto model = QmlDesigner::Model::create("QtQuick/Item", 2, 1); - model->setFileUrl(QUrl::fromLocalFile(filePath)); -#endif - - auto textDocument = std::make_unique(fileToString(filePath)); - - auto modifier = std::make_unique(textDocument.get(), - QTextCursor{textDocument.get()}); - - rewriterView.setTextModifier(modifier.get()); - - model->setRewriterView(&rewriterView); - - auto rootModelNodeMetaInfo = rewriterView.rootModelNode().metaInfo(); - bool is3DRoot = rewriterView.errors().isEmpty() - && (rootModelNodeMetaInfo.isQtQuick3DNode() - || rootModelNodeMetaInfo.isQtQuick3DMaterial()); - - if (!rewriterView.errors().isEmpty() || (!rewriterView.rootModelNode().metaInfo().isGraphicalItem() - && !is3DRoot)) { + QImage captureImage; + if (runProcess(createArguments(name, outFile, auxiliaryData))) { + captureImage.load(outFile); + } else { if (abortCallback) abortCallback(ImageCache::AbortReason::Failed, std::move(flowtoken)); return; } - if (is3DRoot) { - if (auto libIcon = std::get_if(&auxiliaryData)) - rewriterView.rootModelNode().setAuxiliaryData(AuxiliaryDataType::NodeInstancePropertyOverwrite, - "isLibraryIcon", - libIcon->enable); - } - - ModelNode stateNode = rewriterView.modelNodeForId(QString{state}); - - if (stateNode.isValid()) - rewriterView.setCurrentStateNode(stateNode); - - QImage captureImage; - - auto callback = [&](const QImage &image) { captureImage = image; }; - - if (!m_target) - return; - - nodeInstanceView.setTarget(m_target.data()); - m_connectionManager.setCallback(std::move(callback)); - bool isCrashed = false; - nodeInstanceView.setCrashCallback([&] { isCrashed = true; }); - model->setNodeInstanceView(&nodeInstanceView); - - bool capturedDataArrived = m_connectionManager.waitForCapturedData(); - - m_connectionManager.setCallback({}); - m_connectionManager.setCrashCallback({}); - - model->setNodeInstanceView({}); - model->setRewriterView({}); - - if (isCrashed) - abortCallback(ImageCache::AbortReason::Failed, std::move(flowtoken)); - - if (!capturedDataArrived && abortCallback) - abortCallback(ImageCache::AbortReason::Failed, std::move(flowtoken)); - if (nullImageHandling == ImageCacheCollectorNullImageHandling::CaptureNullImage || !captureImage.isNull()) { QImage midSizeImage = scaleImage(captureImage, QSize{300, 300}); QImage smallImage = scaleImage(midSizeImage, QSize{96, 96}); captureCallback(captureImage, midSizeImage, smallImage, std::move(flowtoken)); + } else { + if (abortCallback) + abortCallback(ImageCache::AbortReason::Failed, std::move(flowtoken)); + return; } } @@ -193,4 +107,72 @@ ProjectExplorer::Target *ImageCacheCollector::target() const return m_target; } +QStringList ImageCacheCollector::createArguments(Utils::SmallStringView name, + const QString &outFile, + const ImageCache::AuxiliaryData &auxiliaryData) const +{ + QStringList arguments; + const QString filePath{name}; + + arguments.append("--qml-renderer"); + arguments.append(filePath); + + if (m_target && m_target->project()) { + arguments.append("-i"); + arguments.append(m_target->project()->projectDirectory().toFSPathString()); + } + + arguments.append("-o"); + arguments.append(outFile); + + if (std::holds_alternative(auxiliaryData)) + arguments.append("--libIcon"); + + if (captureImageMinimumSize.isValid()) { + arguments.append("--minW"); + arguments.append(QString::number(captureImageMinimumSize.width())); + arguments.append("--minH"); + arguments.append(QString::number(captureImageMinimumSize.height())); + } + + if (captureImageMaximumSize.isValid()) { + arguments.append("--maxW"); + arguments.append(QString::number(captureImageMaximumSize.width())); + arguments.append("--maxH"); + arguments.append(QString::number(captureImageMaximumSize.height())); + } + + return arguments; +} + +bool ImageCacheCollector::runProcess(const QStringList &arguments) const +{ + if (!m_target) + return false; + + auto [workingDirectoryPath, puppetPath] = QmlDesigner::QmlPuppetPaths::qmlPuppetPaths( + target(), m_externalDependencies.designerSettings()); + if (puppetPath.isEmpty()) + return false; + + QProcessUniquePointer puppetProcess{new QProcess}; + + QObject::connect(QCoreApplication::instance(), + &QCoreApplication::aboutToQuit, + puppetProcess.get(), + &QProcess::kill); + + puppetProcess->setWorkingDirectory(workingDirectoryPath.toFSPathString()); + puppetProcess->setProcessChannelMode(QProcess::ForwardedChannels); + + puppetProcess->start(puppetPath.toFSPathString(), arguments); + + if (puppetProcess->waitForFinished(30000)) { + return puppetProcess->exitStatus() == QProcess::ExitStatus::NormalExit + && puppetProcess->exitCode() == 0; + } + + return false; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/imagecachecollectors/imagecachecollector.h b/src/plugins/qmldesigner/imagecachecollectors/imagecachecollector.h index e6250393b2a..47ab9e8ab2e 100644 --- a/src/plugins/qmldesigner/imagecachecollectors/imagecachecollector.h +++ b/src/plugins/qmldesigner/imagecachecollectors/imagecachecollector.h @@ -5,25 +5,14 @@ #include -#include - #include -QT_BEGIN_NAMESPACE -class QTextDocument; -QT_END_NAMESPACE - namespace ProjectExplorer { class Target; } namespace QmlDesigner { -class Model; -class NotIndentingTextEditModifier; -class ImageCacheConnectionManager; -class RewriterView; -class NodeInstanceView; class ExternalDependenciesInterface; enum class ImageCacheCollectorNullImageHandling { CaptureNullImage, DontCaptureNullImage }; @@ -31,8 +20,7 @@ enum class ImageCacheCollectorNullImageHandling { CaptureNullImage, DontCaptureN class ImageCacheCollector final : public ImageCacheCollectorInterface { public: - ImageCacheCollector(ImageCacheConnectionManager &connectionManager, - QSize captureImageMinimumSize, + ImageCacheCollector(QSize captureImageMinimumSize, QSize captureImageMaximumSize, ExternalDependenciesInterface &externalDependencies, ImageCacheCollectorNullImageHandling nullImageHandling = {}); @@ -58,16 +46,17 @@ public: ProjectExplorer::Target *target() const; private: - ImageCacheConnectionManager &m_connectionManager; + bool runProcess(const QStringList &arguments) const; + QStringList createArguments(Utils::SmallStringView name, + const QString &outFile, + const ImageCache::AuxiliaryData &auxiliaryData) const; + +private: QPointer m_target; QSize captureImageMinimumSize; QSize captureImageMaximumSize; ExternalDependenciesInterface &m_externalDependencies; ImageCacheCollectorNullImageHandling nullImageHandling{}; -#ifdef QDS_USE_PROJECTSTORAGE - ProjectStorageType *m_projectStorage = nullptr; - PathCacheType *m_pathCache = nullptr; -#endif }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/imagecachecollectors/imagecacheconnectionmanager.cpp b/src/plugins/qmldesigner/imagecachecollectors/imagecacheconnectionmanager.cpp deleted file mode 100644 index f8a41b620bb..00000000000 --- a/src/plugins/qmldesigner/imagecachecollectors/imagecacheconnectionmanager.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "imagecacheconnectionmanager.h" - -#include - -#include - -namespace QmlDesigner { - -ImageCacheConnectionManager::ImageCacheConnectionManager() -{ - connections().emplace_back("Capture icon", "captureiconmode"); -} - -void ImageCacheConnectionManager::setCallback(ImageCacheConnectionManager::Callback callback) -{ - m_captureCallback = std::move(callback); -} - -bool ImageCacheConnectionManager::waitForCapturedData() -{ - if (connections().empty()) - return false; - - disconnect(connections().front().socket.get(), &QIODevice::readyRead, nullptr, nullptr); - - while (!m_capturedDataArrived) { - if (!(connections().front().socket)) - return false; - bool dataArrived = connections().front().socket->waitForReadyRead(10000); - - if (!dataArrived) - return false; - - readDataStream(connections().front()); - } - - m_capturedDataArrived = false; - - return true; -} - -void ImageCacheConnectionManager::dispatchCommand(const QVariant &command, - ConnectionManagerInterface::Connection &) -{ - static const int capturedDataCommandType = QMetaType::fromName("CapturedDataCommand").id(); - - if (command.typeId() == capturedDataCommandType) { - m_captureCallback(command.value().image); - m_capturedDataArrived = true; - } -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/imagecachecollectors/imagecacheconnectionmanager.h b/src/plugins/qmldesigner/imagecachecollectors/imagecacheconnectionmanager.h deleted file mode 100644 index 0a881665293..00000000000 --- a/src/plugins/qmldesigner/imagecachecollectors/imagecacheconnectionmanager.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include - -namespace QmlDesigner { - -class CapturedDataCommand; - -class ImageCacheConnectionManager : public ConnectionManager -{ -public: - using Callback = std::function; - - ImageCacheConnectionManager(); - - void setCallback(Callback captureCallback); - - bool waitForCapturedData(); - -protected: - void dispatchCommand(const QVariant &command, Connection &connection) override; - -private: - Callback m_captureCallback; - bool m_capturedDataArrived = false; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/imagecachecollectors/meshimagecachecollector.cpp b/src/plugins/qmldesigner/imagecachecollectors/meshimagecachecollector.cpp index 0e0547c1935..690780a24ec 100644 --- a/src/plugins/qmldesigner/imagecachecollectors/meshimagecachecollector.cpp +++ b/src/plugins/qmldesigner/imagecachecollectors/meshimagecachecollector.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "meshimagecachecollector.h" -#include "imagecacheconnectionmanager.h" #include #include @@ -12,13 +11,11 @@ namespace QmlDesigner { -MeshImageCacheCollector::MeshImageCacheCollector(ImageCacheConnectionManager &connectionManager, - QSize captureImageMinimumSize, +MeshImageCacheCollector::MeshImageCacheCollector(QSize captureImageMinimumSize, QSize captureImageMaximumSize, ExternalDependenciesInterface &externalDependencies, ImageCacheCollectorNullImageHandling nullImageHandling) - : m_imageCacheCollector(connectionManager, - captureImageMinimumSize, + : m_imageCacheCollector(captureImageMinimumSize, captureImageMaximumSize, externalDependencies, nullImageHandling) diff --git a/src/plugins/qmldesigner/imagecachecollectors/meshimagecachecollector.h b/src/plugins/qmldesigner/imagecachecollectors/meshimagecachecollector.h index 9836e625e76..5272c317097 100644 --- a/src/plugins/qmldesigner/imagecachecollectors/meshimagecachecollector.h +++ b/src/plugins/qmldesigner/imagecachecollectors/meshimagecachecollector.h @@ -11,13 +11,10 @@ class Target; namespace QmlDesigner { -class ImageCacheConnectionManager; - class MeshImageCacheCollector final : public ImageCacheCollectorInterface { public: - MeshImageCacheCollector(ImageCacheConnectionManager &connectionManager, - QSize captureImageMinimumSize, + MeshImageCacheCollector(QSize captureImageMinimumSize, QSize captureImageMaximumSize, ExternalDependenciesInterface &externalDependencies, ImageCacheCollectorNullImageHandling nullImageHandling = {}); diff --git a/src/plugins/qmldesigner/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/instances/nodeinstanceview.cpp index f0af9d8e8e1..839e04be368 100644 --- a/src/plugins/qmldesigner/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/instances/nodeinstanceview.cpp @@ -1272,8 +1272,6 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() m_externalDependencies.currentResourcePath(), sceneStates, lastUsedLanguage, - m_captureImageMinimumSize, - m_captureImageMaximumSize, stateInstanceId); } diff --git a/src/plugins/qmldesigner/instances/nodeinstanceview.h b/src/plugins/qmldesigner/instances/nodeinstanceview.h index 798d0c554b2..7a5ea908d94 100644 --- a/src/plugins/qmldesigner/instances/nodeinstanceview.h +++ b/src/plugins/qmldesigner/instances/nodeinstanceview.h @@ -156,13 +156,6 @@ public: m_crashCallback = std::move(crashCallback); } - void setCaptureImageMinimumAndMaximumSize(QSize captureImageMinimumSize, - QSize captureImageMaximumSize) - { - m_captureImageMinimumSize = captureImageMinimumSize; - m_captureImageMaximumSize = captureImageMaximumSize; - } - void startNanotrace(); void endNanotrace(); @@ -313,8 +306,6 @@ private: QHash m_qsbPathToFilterMap; int m_remainingQsbTargets = 0; QTimer m_rotBlockTimer; - QSize m_captureImageMinimumSize{150, 150}; - QSize m_captureImageMaximumSize{1000, 1000}; bool m_qsbEnabled = false; }; diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index 5718ac7ae21..873b604833f 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -33,7 +33,6 @@ #include #include #include -#include #include #include @@ -124,8 +123,8 @@ class QmlDesignerProjectManager::ImageCacheData { public: ImageCacheData(ExternalDependenciesInterface &externalDependencies) - : meshImageCollector{connectionManager, QSize{300, 300}, QSize{600, 600}, externalDependencies} - , nodeInstanceCollector{connectionManager, QSize{300, 300}, QSize{600, 600}, externalDependencies} + : meshImageCollector{QSize{300, 300}, QSize{600, 600}, externalDependencies} + , nodeInstanceCollector{QSize{300, 300}, QSize{600, 600}, externalDependencies} {} public: @@ -134,7 +133,6 @@ public: Sqlite::JournalMode::Wal, Sqlite::LockingMode::Normal}; ImageCacheStorage storage{database}; - ImageCacheConnectionManager connectionManager; MeshImageCacheCollector meshImageCollector; TextureImageCacheCollector textureImageCollector; ImageCacheCollector nodeInstanceCollector; @@ -153,8 +151,7 @@ class QmlDesignerProjectManager::PreviewImageCacheData { public: PreviewImageCacheData(ExternalDependenciesInterface &externalDependencies) - : collector{connectionManager, - QSize{300, 300}, + : collector{QSize{300, 300}, QSize{1000, 1000}, externalDependencies, ImageCacheCollectorNullImageHandling::CaptureNullImage} @@ -168,7 +165,6 @@ public: Sqlite::JournalMode::Wal, Sqlite::LockingMode::Normal}; ImageCacheStorage storage{database}; - ImageCacheConnectionManager connectionManager; ImageCacheCollector collector; PreviewTimeStampProvider timeStampProvider; AsynchronousExplicitImageCache cache{storage}; @@ -255,8 +251,7 @@ public: ::ProjectExplorer::Project *project, PathCacheType &pathCache, ExternalDependenciesInterface &externalDependencies) - : collector{connectionManager, - QSize{300, 300}, + : collector{QSize{300, 300}, QSize{1000, 1000}, externalDependencies, ImageCacheCollectorNullImageHandling::CaptureNullImage} @@ -264,7 +259,6 @@ public: , projectStorageData{createProjectStorageData(project, pathCache)} {} - ImageCacheConnectionManager connectionManager; ImageCacheCollector collector; PreviewTimeStampProvider timeStampProvider; AsynchronousImageFactory factory; diff --git a/src/tools/qmlpuppet/CMakeLists.txt b/src/tools/qmlpuppet/CMakeLists.txt index 9f20bbe3d9e..2c0ff6a1f50 100644 --- a/src/tools/qmlpuppet/CMakeLists.txt +++ b/src/tools/qmlpuppet/CMakeLists.txt @@ -168,7 +168,6 @@ extend_qtc_executable(qmlpuppet qt5capturepreviewnodeinstanceserver.cpp qt5capturepreviewnodeinstanceserver.h nodeinstanceserverdispatcher.cpp nodeinstanceserverdispatcher.h capturenodeinstanceserverdispatcher.cpp capturenodeinstanceserverdispatcher.h - qt5captureimagenodeinstanceserver.cpp qt5captureimagenodeinstanceserver.h viewconfig.cpp viewconfig.h animationdriver.cpp animationdriver.h ) diff --git a/src/tools/qmlpuppet/qmlpuppet/instances/instances.pri b/src/tools/qmlpuppet/qmlpuppet/instances/instances.pri index 505dd748e49..9f1ce87e347 100644 --- a/src/tools/qmlpuppet/qmlpuppet/instances/instances.pri +++ b/src/tools/qmlpuppet/qmlpuppet/instances/instances.pri @@ -22,7 +22,6 @@ versionAtLeast(QT_VERSION, 5.15.0) { HEADERS += $$PWD/qt5nodeinstanceserver.h \ $$PWD/capturenodeinstanceserverdispatcher.h \ $$PWD/nodeinstanceserverdispatcher.h \ - $$PWD/qt5captureimagenodeinstanceserver.h \ $$PWD/qt5capturepreviewnodeinstanceserver.h \ $$PWD/qt5testnodeinstanceserver.h \ $$PWD/qt5bakelightsnodeinstanceserver.h \ @@ -58,7 +57,6 @@ HEADERS += $$PWD/qt5nodeinstanceserver.h \ SOURCES += $$PWD/qt5nodeinstanceserver.cpp \ $$PWD/capturenodeinstanceserverdispatcher.cpp \ $$PWD/nodeinstanceserverdispatcher.cpp \ - $$PWD/qt5captureimagenodeinstanceserver.cpp \ $$PWD/qt5capturepreviewnodeinstanceserver.cpp \ $$PWD/qt5testnodeinstanceserver.cpp \ $$PWD/qt5bakelightsnodeinstanceserver.cpp \ diff --git a/src/tools/qmlpuppet/qmlpuppet/instances/nodeinstanceserverdispatcher.cpp b/src/tools/qmlpuppet/qmlpuppet/instances/nodeinstanceserverdispatcher.cpp index 9e3cdac151e..c7f1f053fe6 100644 --- a/src/tools/qmlpuppet/qmlpuppet/instances/nodeinstanceserverdispatcher.cpp +++ b/src/tools/qmlpuppet/qmlpuppet/instances/nodeinstanceserverdispatcher.cpp @@ -4,7 +4,6 @@ #include "nodeinstanceserverdispatcher.h" #include "qt5bakelightsnodeinstanceserver.h" -#include "qt5captureimagenodeinstanceserver.h" #include "qt5capturepreviewnodeinstanceserver.h" #include "qt5import3dnodeinstanceserver.h" #include "qt5informationnodeinstanceserver.h" @@ -164,8 +163,6 @@ std::unique_ptr createNodeInstanceServer( { if (serverName == "capturemode") return std::make_unique(nodeInstanceClient); - else if (serverName == "captureiconmode") - return std::make_unique(nodeInstanceClient); else if (serverName == "rendermode") return std::make_unique(nodeInstanceClient); else if (serverName == "editormode") diff --git a/src/tools/qmlpuppet/qmlpuppet/instances/qt5captureimagenodeinstanceserver.cpp b/src/tools/qmlpuppet/qmlpuppet/instances/qt5captureimagenodeinstanceserver.cpp deleted file mode 100644 index 70625962cc8..00000000000 --- a/src/tools/qmlpuppet/qmlpuppet/instances/qt5captureimagenodeinstanceserver.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "qt5captureimagenodeinstanceserver.h" -#include "servernodeinstance.h" - -#include -#include -#include - -#include -#include -#include - -#include - -namespace QmlDesigner { - -namespace { - -QImage renderImage(ServerNodeInstance rootNodeInstance, QSize minimumSize, QSize maximumSize) -{ - rootNodeInstance.updateDirtyNodeRecursive(); - - QSize previewImageSize = rootNodeInstance.boundingRect().size().toSize(); - if (previewImageSize.isEmpty()) { - previewImageSize = minimumSize; - } else if (previewImageSize.width() < minimumSize.width() - || previewImageSize.height() < minimumSize.height()) { - previewImageSize.scale(minimumSize, Qt::KeepAspectRatio); - } - - if (previewImageSize.width() > maximumSize.width() - || previewImageSize.height() > maximumSize.height()) { - previewImageSize.scale(maximumSize, Qt::KeepAspectRatio); - } - - QImage previewImage = rootNodeInstance.renderPreviewImage(previewImageSize); - - return previewImage; -} -} // namespace - -void Qt5CaptureImageNodeInstanceServer::collectItemChangesAndSendChangeCommands() -{ - static bool inFunction = false; - - if (!rootNodeInstance().holdsGraphical()) { - nodeInstanceClient()->capturedData(CapturedDataCommand{}); - return; - } - - if (!inFunction) { - inFunction = true; - - auto rooNodeInstance = rootNodeInstance(); - if (QQuickItem *qitem = rooNodeInstance.rootQuickItem()) - qitem->setClip(true); - - QQuickDesignerSupport::polishItems(quickWindow()); - - QImage image = renderImage(rooNodeInstance, m_minimumSize, m_maximumSize); - - nodeInstanceClient()->capturedData(CapturedDataCommand{std::move(image)}); - - slowDownRenderTimer(); - inFunction = false; - } -} - -void QmlDesigner::Qt5CaptureImageNodeInstanceServer::createScene(const CreateSceneCommand &command) -{ - m_minimumSize = command.captureImageMinimumSize; - m_maximumSize = command.captureImageMaximumSize; - - Qt5PreviewNodeInstanceServer::createScene(command); -} - -} // namespace diff --git a/src/tools/qmlpuppet/qmlpuppet/instances/qt5captureimagenodeinstanceserver.h b/src/tools/qmlpuppet/qmlpuppet/instances/qt5captureimagenodeinstanceserver.h deleted file mode 100644 index f4a7c32a0c9..00000000000 --- a/src/tools/qmlpuppet/qmlpuppet/instances/qt5captureimagenodeinstanceserver.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include - -namespace QmlDesigner { - -class Qt5CaptureImageNodeInstanceServer : public Qt5PreviewNodeInstanceServer -{ -public: - explicit Qt5CaptureImageNodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient) - : Qt5PreviewNodeInstanceServer(nodeInstanceClient) - {} - - void createScene(const CreateSceneCommand &command) override; - -protected: - void collectItemChangesAndSendChangeCommands() override; - -private: - QSize m_minimumSize; - QSize m_maximumSize; -}; - -} // namespace QmlDesigner diff --git a/src/tools/qmlpuppet/qmlpuppet/instances/qt5nodeinstanceclientproxy.cpp b/src/tools/qmlpuppet/qmlpuppet/instances/qt5nodeinstanceclientproxy.cpp index c11899e0ec9..ee58004af82 100644 --- a/src/tools/qmlpuppet/qmlpuppet/instances/qt5nodeinstanceclientproxy.cpp +++ b/src/tools/qmlpuppet/qmlpuppet/instances/qt5nodeinstanceclientproxy.cpp @@ -7,7 +7,6 @@ #include "capturenodeinstanceserverdispatcher.h" #include "qt5bakelightsnodeinstanceserver.h" -#include "qt5captureimagenodeinstanceserver.h" #include "qt5capturepreviewnodeinstanceserver.h" #include "qt5informationnodeinstanceserver.h" #include "qt5import3dnodeinstanceserver.h" @@ -65,9 +64,6 @@ Qt5NodeInstanceClientProxy::Qt5NodeInstanceClientProxy(QObject *parent) : } else if (QCoreApplication::arguments().at(2) == QLatin1String("capturemode")) { setNodeInstanceServer(std::make_unique(this)); initializeSocket(); - } else if (QCoreApplication::arguments().at(2) == QLatin1String("captureiconmode")) { - setNodeInstanceServer(std::make_unique(this)); - initializeSocket(); } else if (QCoreApplication::arguments().at(2) == QLatin1String("bakelightsmode")) { setNodeInstanceServer(std::make_unique(this)); initializeSocket(); diff --git a/src/tools/qmlpuppet/qmlpuppet/renderer/qmlrenderer.cpp b/src/tools/qmlpuppet/qmlpuppet/renderer/qmlrenderer.cpp index 2fd9487276a..3b8cff54c15 100644 --- a/src/tools/qmlpuppet/qmlpuppet/renderer/qmlrenderer.cpp +++ b/src/tools/qmlpuppet/qmlpuppet/renderer/qmlrenderer.cpp @@ -16,6 +16,9 @@ #include #include +constexpr int DEFAULT_RENDER_DIM = 300; +constexpr int DEFAULT_MAX_DIM = 1024; + void QmlRenderer::initCoreApp() { #if defined QT_WIDGETS_LIB @@ -36,15 +39,29 @@ void QmlRenderer::populateParser() "Output image file path.", "path"}, - // "h" is reserved arg for help, so use capital letters for height/width - {QStringList{"H", "height"}, - "Height of the final rendered image.", + // The qml component is rendered at its preferred size if available + // Min/max dimensions specify a range of acceptable final scaled sizes + // If the size of rendered item is outside the min/max range, it is cropped in final scaling + {QStringList{"minW"}, + "Minimum width of the final scaled rendered image.", "pixels"}, - {QStringList{"W", "width"}, - "Width of the final rendered image.", + {QStringList{"minH"}, + "Minimum height of the final scaled rendered image.", "pixels"}, + {QStringList{"maxW"}, + QString("Maximum width of the final scaled rendered image."), + "pixels"}, + + {QStringList{"maxH"}, + "Maximum height of the final scaled rendered image.", + "pixels"}, + + {QStringList{"libIcon"}, + "Render library icon rather than regular preview." + "It zooms 3D objects bit more aggressively and suppresses the background."}, + {QStringList{"v", "verbose"}, "Display additional output."} }); @@ -56,10 +73,14 @@ void QmlRenderer::initQmlRunner() { if (m_argParser.isSet("importpath")) m_importPaths = m_argParser.value("importpath").split(";"); - if (m_argParser.isSet("width")) - m_requestedSize.setWidth(m_argParser.value("width").toInt()); - if (m_argParser.isSet("height")) - m_requestedSize.setHeight(m_argParser.value("height").toInt()); + if (m_argParser.isSet("minW")) + m_reqMinSize.setWidth(m_argParser.value("minW").toInt()); + if (m_argParser.isSet("minH")) + m_reqMinSize.setHeight(m_argParser.value("minH").toInt()); + if (m_argParser.isSet("maxW")) + m_reqMaxSize.setWidth(m_argParser.value("maxW").toInt()); + if (m_argParser.isSet("maxH")) + m_reqMaxSize.setHeight(m_argParser.value("maxH").toInt()); if (m_argParser.isSet("verbose")) m_verbose = true; @@ -76,17 +97,18 @@ void QmlRenderer::initQmlRunner() else m_outFile = m_sourceFile + ".png"; - if (m_requestedSize.width() <= 0) - m_requestedSize.setWidth(150); - if (m_requestedSize.height() <= 0) - m_requestedSize.setHeight(150); + if (m_argParser.isSet("libIcon")) + m_isLibIcon = true; if (m_verbose) { - info(QString("Import path = %1").arg(m_importPaths.join(";"))); - info(QString("Requested size = %1 x %2").arg(m_requestedSize.width()) - .arg(m_requestedSize.height())); - info(QString("Source file = %1").arg(m_sourceFile)); - info(QString("Output file = %1").arg(m_outFile)); + info(QString("Import path = %1").arg(m_importPaths.join(";"))); + info(QString("Requested min size = %1 x %2").arg(m_reqMinSize.width()) + .arg(m_reqMinSize.height())); + info(QString("Requested max size = %1 x %2").arg(m_reqMaxSize.width()) + .arg(m_reqMaxSize.height())); + info(QString("Source file = %1").arg(m_sourceFile)); + info(QString("Output file = %1").arg(m_outFile)); + info(QString("Is library icon = %1").arg(m_isLibIcon ? QString("true") : QString("false"))); } if (setupRenderer()) { @@ -140,6 +162,9 @@ bool QmlRenderer::setupRenderer() } m_containerItem->setParentItem(m_window->contentItem()); + if (m_isLibIcon) + QMetaObject::invokeMethod(m_containerItem, "setIconMode", Q_ARG(QVariant, true)); + contentItem3D = QQmlProperty::read(m_containerItem, "contentItem").value(); if (qobject_cast(renderObj)) { QMetaObject::invokeMethod( @@ -155,28 +180,25 @@ bool QmlRenderer::setupRenderer() } m_is3D = true; - m_renderSize = m_requestedSize; - contentItem3D->setSize(m_requestedSize); + setRenderSize({}); + contentItem3D->setSize(m_renderSize); } else #endif // QUICK3D_MODULE if (auto renderItem = qobject_cast(renderObj)) { - m_renderSize = renderItem->size().toSize(); - if (m_renderSize.width() <= 0) - m_renderSize.setWidth(m_requestedSize.width()); - if (m_renderSize.height() <= 0) - m_renderSize.setHeight(m_requestedSize.height()); + setRenderSize(renderItem->size().toSize()); renderItem->setSize(m_renderSize); renderItem->setParentItem(m_window->contentItem()); // When rendering 2D scenes, we just render the given QML without extra container m_containerItem = renderItem; } else if (auto renderWindow = qobject_cast(renderObj)) { // Hack to render Window items: reparent window content to m_window->contentItem() - m_renderSize = renderWindow->size(); + setRenderSize(renderWindow->size()); m_containerItem = m_window->contentItem(); // Suppress the original window. // Offscreen position ensures we don't get even brief flash of it. - renderWindow->setPosition(-100000, -100000); renderWindow->setVisible(false); + renderWindow->resize(2, 2); + renderWindow->setPosition(-10000, -10000); const QList childItems = renderWindow->contentItem()->childItems(); for (QQuickItem *item : childItems) { item->setParent(m_window->contentItem()); @@ -274,12 +296,32 @@ void QmlRenderer::render() QImage wrapperImage(reinterpret_cast(readResult.data.constData()), readResult.pixelSize.width(), readResult.pixelSize.height(), QImage::Format_RGBA8888_Premultiplied); - if (m_rhi->isYUpInFramebuffer()) - renderImage = wrapperImage.mirrored().scaled(m_requestedSize, Qt::IgnoreAspectRatio, + + QSize maxSize = m_reqMaxSize; + if (maxSize.width() <= 0) { + m_reqMinSize.width() > DEFAULT_MAX_DIM ? maxSize.setWidth(m_reqMinSize.width()) + : maxSize.setWidth(DEFAULT_MAX_DIM); + } + if (maxSize.height() <= 0) { + m_reqMinSize.height() > DEFAULT_MAX_DIM ? maxSize.setHeight(m_reqMinSize.height()) + : maxSize.setHeight(DEFAULT_MAX_DIM); + } + + QSize scaledSize = m_renderSize.scaled(m_renderSize.expandedTo(m_reqMinSize).boundedTo(maxSize), + Qt::KeepAspectRatio); + + info(QString("Rendered size = %1 x %2").arg(m_renderSize.width()) + .arg(m_renderSize.height())); + info(QString("Scaled size = %1 x %2").arg(scaledSize.width()) + .arg(scaledSize.height())); + + if (m_rhi->isYUpInFramebuffer()) { + renderImage = wrapperImage.mirrored().scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - else - renderImage = wrapperImage.copy().scaled(m_requestedSize, Qt::IgnoreAspectRatio, + } else { + renderImage = wrapperImage.copy().scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + } }; QRhiResourceUpdateBatch *readbackBatch = m_rhi->nextResourceUpdateBatch(); readbackBatch->readBackTexture(m_texture.get(), &readResult); @@ -316,3 +358,16 @@ void QmlRenderer::asyncQuit(int errorCode) exit(errorCode); }); } + +void QmlRenderer::setRenderSize(QSize size) +{ + m_renderSize = size; + if (m_renderSize.width() <= 0) { + m_reqMaxSize.width() > 0 ? m_renderSize.setWidth(m_reqMaxSize.width()) + : m_renderSize.setWidth(DEFAULT_RENDER_DIM); + } + if (m_renderSize.height() <= 0) { + m_reqMaxSize.height() > 0 ? m_renderSize.setHeight(m_reqMaxSize.height()) + : m_renderSize.setHeight(DEFAULT_RENDER_DIM); + } +} diff --git a/src/tools/qmlpuppet/qmlpuppet/renderer/qmlrenderer.h b/src/tools/qmlpuppet/qmlpuppet/renderer/qmlrenderer.h index 212f57074d3..9e5b2c99e23 100644 --- a/src/tools/qmlpuppet/qmlpuppet/renderer/qmlrenderer.h +++ b/src/tools/qmlpuppet/qmlpuppet/renderer/qmlrenderer.h @@ -34,14 +34,18 @@ private: void error(const QString &msg); void asyncQuit(int errorCode); + void setRenderSize(QSize size); + QStringList m_importPaths; - QSize m_requestedSize; + QSize m_reqMinSize; + QSize m_reqMaxSize; QSize m_renderSize; QString m_sourceFile; QString m_outFile; bool m_verbose = false; bool m_is3D = false; bool m_fit3D = false; + bool m_isLibIcon = false; QQuickItem *m_containerItem = nullptr; QRhi *m_rhi = nullptr;