forked from qt-creator/qt-creator
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 <mahmoud.badri@qt.io> Reviewed-by: Marco Bubke <marco.bubke@qt.io>
This commit is contained in:
@@ -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.
|
||||
|
@@ -36,8 +36,6 @@ public:
|
||||
const QUrl &resourceUrl,
|
||||
const QHash<QString, QVariantMap> &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<QString, QVariantMap> edit3dToolStates;
|
||||
QString language;
|
||||
QSize captureImageMinimumSize;
|
||||
QSize captureImageMaximumSize;
|
||||
qint32 stateInstanceId = 0;
|
||||
};
|
||||
|
||||
|
@@ -517,8 +517,6 @@ extend_qtc_plugin(QmlDesigner
|
||||
SOURCES
|
||||
imagecachecollector.cpp
|
||||
imagecachecollector.h
|
||||
imagecacheconnectionmanager.cpp
|
||||
imagecacheconnectionmanager.h
|
||||
imagecachefontcollector.cpp
|
||||
imagecachefontcollector.h
|
||||
meshimagecachecollector.cpp
|
||||
|
@@ -13,7 +13,6 @@
|
||||
#include <imagecache/imagecachestorage.h>
|
||||
#include <imagecache/timestampprovider.h>
|
||||
#include <imagecachecollectors/imagecachecollector.h>
|
||||
#include <imagecachecollectors/imagecacheconnectionmanager.h>
|
||||
#include <imagecachecollectors/imagecachefontcollector.h>
|
||||
#include <modelnodeoperations.h>
|
||||
#include <nodelistproperty.h>
|
||||
|
@@ -12,7 +12,6 @@
|
||||
#include <imagecache/imagecachestorage.h>
|
||||
#include <imagecache/timestampprovider.h>
|
||||
#include <imagecachecollectors/imagecachecollector.h>
|
||||
#include <imagecachecollectors/imagecacheconnectionmanager.h>
|
||||
|
||||
#include <sqlitedatabase.h>
|
||||
|
||||
|
@@ -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 <model.h>
|
||||
#include <nodeinstanceview.h>
|
||||
#include <nodemetainfo.h>
|
||||
#include <plaintexteditmodifier.h>
|
||||
#include <rewriterview.h>
|
||||
#include <externaldependenciesinterface.h>
|
||||
#include <qmlpuppetpaths.h>
|
||||
#include <qprocessuniqueptr.h>
|
||||
|
||||
#include <projectexplorer/project.h>
|
||||
#include <projectexplorer/target.h>
|
||||
#include <utils/fileutils.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QGuiApplication>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QProcess>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
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<QTextDocument>(fileToString(filePath));
|
||||
|
||||
auto modifier = std::make_unique<NotIndentingTextEditModifier>(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<ImageCache::LibraryIconAuxiliaryData>(&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<ImageCache::LibraryIconAuxiliaryData>(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
|
||||
|
@@ -5,25 +5,14 @@
|
||||
|
||||
#include <imagecache/imagecachecollectorinterface.h>
|
||||
|
||||
#include <modelfwd.h>
|
||||
|
||||
#include <QPointer>
|
||||
|
||||
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<ProjectExplorer::Target> 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
|
||||
|
@@ -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 <captureddatacommand.h>
|
||||
|
||||
#include <QLocalSocket>
|
||||
|
||||
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<CapturedDataCommand>().image);
|
||||
m_capturedDataArrived = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
@@ -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 <instances/connectionmanager.h>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class CapturedDataCommand;
|
||||
|
||||
class ImageCacheConnectionManager : public ConnectionManager
|
||||
{
|
||||
public:
|
||||
using Callback = std::function<void(const QImage &)>;
|
||||
|
||||
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
|
@@ -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 <projectexplorer/target.h>
|
||||
#include <utils/smallstring.h>
|
||||
@@ -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)
|
||||
|
@@ -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 = {});
|
||||
|
@@ -1272,8 +1272,6 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand()
|
||||
m_externalDependencies.currentResourcePath(),
|
||||
sceneStates,
|
||||
lastUsedLanguage,
|
||||
m_captureImageMinimumSize,
|
||||
m_captureImageMaximumSize,
|
||||
stateInstanceId);
|
||||
}
|
||||
|
||||
|
@@ -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<QString, QStringList> m_qsbPathToFilterMap;
|
||||
int m_remainingQsbTargets = 0;
|
||||
QTimer m_rotBlockTimer;
|
||||
QSize m_captureImageMinimumSize{150, 150};
|
||||
QSize m_captureImageMaximumSize{1000, 1000};
|
||||
bool m_qsbEnabled = false;
|
||||
};
|
||||
|
||||
|
@@ -33,7 +33,6 @@
|
||||
#include <imagecache/imagecachestorage.h>
|
||||
#include <imagecache/timestampprovider.h>
|
||||
#include <imagecachecollectors/imagecachecollector.h>
|
||||
#include <imagecachecollectors/imagecacheconnectionmanager.h>
|
||||
#include <imagecachecollectors/meshimagecachecollector.h>
|
||||
#include <imagecachecollectors/textureimagecachecollector.h>
|
||||
|
||||
@@ -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<Sqlite::Database> 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<Sqlite::Database> 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;
|
||||
|
@@ -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
|
||||
)
|
||||
|
@@ -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 \
|
||||
|
@@ -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<NodeInstanceServer> createNodeInstanceServer(
|
||||
{
|
||||
if (serverName == "capturemode")
|
||||
return std::make_unique<Qt5CapturePreviewNodeInstanceServer>(nodeInstanceClient);
|
||||
else if (serverName == "captureiconmode")
|
||||
return std::make_unique<Qt5CaptureImageNodeInstanceServer>(nodeInstanceClient);
|
||||
else if (serverName == "rendermode")
|
||||
return std::make_unique<Qt5RenderNodeInstanceServer>(nodeInstanceClient);
|
||||
else if (serverName == "editormode")
|
||||
|
@@ -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 <captureddatacommand.h>
|
||||
#include <createscenecommand.h>
|
||||
#include <nodeinstanceclientinterface.h>
|
||||
|
||||
#include <QImage>
|
||||
#include <QQuickItem>
|
||||
#include <QQuickView>
|
||||
|
||||
#include <private/qquickdesignersupport_p.h>
|
||||
|
||||
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
|
@@ -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 <qt5previewnodeinstanceserver.h>
|
||||
|
||||
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
|
@@ -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<Qt5CapturePreviewNodeInstanceServer>(this));
|
||||
initializeSocket();
|
||||
} else if (QCoreApplication::arguments().at(2) == QLatin1String("captureiconmode")) {
|
||||
setNodeInstanceServer(std::make_unique<Qt5CaptureImageNodeInstanceServer>(this));
|
||||
initializeSocket();
|
||||
} else if (QCoreApplication::arguments().at(2) == QLatin1String("bakelightsmode")) {
|
||||
setNodeInstanceServer(std::make_unique<Qt5BakeLightsNodeInstanceServer>(this));
|
||||
initializeSocket();
|
||||
|
@@ -16,6 +16,9 @@
|
||||
#include <QFileInfo>
|
||||
#include <QQmlComponent>
|
||||
|
||||
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("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<QQuickItem *>();
|
||||
if (qobject_cast<QQuick3DNode *>(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<QQuickItem *>(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<QQuickWindow *>(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<QQuickItem *> childItems = renderWindow->contentItem()->childItems();
|
||||
for (QQuickItem *item : childItems) {
|
||||
item->setParent(m_window->contentItem());
|
||||
@@ -274,12 +296,32 @@ void QmlRenderer::render()
|
||||
QImage wrapperImage(reinterpret_cast<const uchar *>(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);
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user