Implement component preview image decorator

master
Jeremy HU 2022-10-05 22:34:56 +11:00
parent bc82539cf2
commit 24e675911e
8 changed files with 159 additions and 9 deletions

View File

@ -104,6 +104,8 @@ HEADERS += sources/component_list_model.h
SOURCES += sources/component_list_model.cc SOURCES += sources/component_list_model.cc
HEADERS += sources/component_preview_grid_widget.h HEADERS += sources/component_preview_grid_widget.h
SOURCES += sources/component_preview_grid_widget.cc SOURCES += sources/component_preview_grid_widget.cc
HEADERS += sources/component_preview_images_decorator.h
SOURCES += sources/component_preview_images_decorator.cc
HEADERS += sources/cut_face_preview.h HEADERS += sources/cut_face_preview.h
SOURCES += sources/cut_face_preview.cc SOURCES += sources/cut_face_preview.cc
HEADERS += sources/dds_file.h HEADERS += sources/dds_file.h

View File

@ -7,7 +7,7 @@ ComponentListModel::ComponentListModel(const Document *document, QObject *parent
QAbstractListModel(parent), QAbstractListModel(parent),
m_document(document) m_document(document)
{ {
connect(m_document, &Document::componentPreviewImageChanged, [this](const dust3d::Uuid &componentId) { connect(m_document, &Document::componentPreviewPixmapChanged, [this](const dust3d::Uuid &componentId) {
// FIXME: dont refresh the whole layout // FIXME: dont refresh the whole layout
emit this->layoutChanged(); emit this->layoutChanged();
}); });

View File

@ -0,0 +1,44 @@
#include <dust3d/base/debug.h>
#include <QPainter>
#include "theme.h"
#include "component_preview_images_decorator.h"
ComponentPreviewImagesDecorator::ComponentPreviewImagesDecorator(std::unique_ptr<std::vector<PreviewInput>> previewInputs)
{
m_previewInputs = std::move(previewInputs);
}
std::unique_ptr<std::unordered_map<dust3d::Uuid, std::unique_ptr<QImage>>> ComponentPreviewImagesDecorator::takeResultImages()
{
return std::move(m_resultImages);
}
void ComponentPreviewImagesDecorator::decorate()
{
if (nullptr == m_previewInputs)
return;
m_resultImages = std::make_unique<std::unordered_map<dust3d::Uuid, std::unique_ptr<QImage>>>();
for (auto &it: *m_previewInputs) {
if (it.isDirectory) {
QPainter painter(it.image.get());
painter.setRenderHints(QPainter::Antialiasing);
QPolygonF polygon;
polygon << QPointF(it.image->width() / 4, 0.0) << QPointF(it.image->width() / 2.5, 0.0);
polygon << QPointF(0.0, it.image->height() / 2.5) << QPointF(0.0, it.image->height() / 4);
QPainterPath painterPath;
painterPath.addPolygon(polygon);
painter.setBrush(Theme::white);
painter.setPen(Qt::NoPen);
painter.drawPath(painterPath);
}
m_resultImages->emplace(it.id, std::move(it.image));
}
}
void ComponentPreviewImagesDecorator::process()
{
decorate();
emit finished();
}

View File

@ -0,0 +1,32 @@
#ifndef DUST3D_APPLICATION_COMPONENT_PREVIEW_IMAGES_DECORATOR_H_
#define DUST3D_APPLICATION_COMPONENT_PREVIEW_IMAGES_DECORATOR_H_
#include <memory>
#include <unordered_map>
#include <dust3d/base/uuid.h>
#include <QImage>
class ComponentPreviewImagesDecorator: public QObject
{
Q_OBJECT
public:
struct PreviewInput
{
dust3d::Uuid id;
std::unique_ptr<QImage> image;
bool isDirectory = false;
};
ComponentPreviewImagesDecorator(std::unique_ptr<std::vector<PreviewInput>> previewInputs);
std::unique_ptr<std::unordered_map<dust3d::Uuid, std::unique_ptr<QImage>>> takeResultImages();
signals:
void finished();
public slots:
void process();
private:
std::unique_ptr<std::vector<PreviewInput>> m_previewInputs;
std::unique_ptr<std::unordered_map<dust3d::Uuid, std::unique_ptr<QImage>>> m_resultImages;
void decorate();
};
#endif

View File

@ -22,6 +22,7 @@
#include <QFormLayout> #include <QFormLayout>
#include <QTextBrowser> #include <QTextBrowser>
#include <QMimeData> #include <QMimeData>
#include <QPixmap>
#include <dust3d/base/ds3_file.h> #include <dust3d/base/ds3_file.h>
#include <dust3d/base/snapshot.h> #include <dust3d/base/snapshot.h>
#include <dust3d/base/snapshot_xml.h> #include <dust3d/base/snapshot_xml.h>
@ -1197,9 +1198,10 @@ void DocumentWindow::generateComponentPreviewImages()
QThread *thread = new QThread; QThread *thread = new QThread;
m_componentPreviewImagesGenerator = new MeshPreviewImagesGenerator(new ModelOffscreenRender(m_modelRenderWidget->format())); m_componentPreviewImagesGenerator = new MeshPreviewImagesGenerator(new ModelOffscreenRender(m_modelRenderWidget->format()));
for (const auto &component: m_document->componentMap) { for (auto &component: m_document->componentMap) {
if (!component.second.isPreviewMeshObsolete) if (!component.second.isPreviewMeshObsolete)
continue; continue;
component.second.isPreviewMeshObsolete = false;
m_componentPreviewImagesGenerator->addInput(component.first, std::unique_ptr<ModelMesh>(component.second.takePreviewMesh())); m_componentPreviewImagesGenerator->addInput(component.first, std::unique_ptr<ModelMesh>(component.second.takePreviewMesh()));
} }
m_componentPreviewImagesGenerator->moveToThread(thread); m_componentPreviewImagesGenerator->moveToThread(thread);
@ -1216,9 +1218,11 @@ void DocumentWindow::componentPreviewImagesReady()
componentImages.reset(m_componentPreviewImagesGenerator->takeImages()); componentImages.reset(m_componentPreviewImagesGenerator->takeImages());
if (nullptr != componentImages) { if (nullptr != componentImages) {
for (const auto &it: *componentImages) { for (const auto &it: *componentImages) {
m_document->setComponentPreviewImage(it.first, it.second); m_document->setComponentPreviewImage(it.first, std::make_unique<QImage>(std::move(it.second)));
} }
} }
decorateComponentPreviewImages();
delete m_componentPreviewImagesGenerator; delete m_componentPreviewImagesGenerator;
m_componentPreviewImagesGenerator = nullptr; m_componentPreviewImagesGenerator = nullptr;
@ -1227,6 +1231,56 @@ void DocumentWindow::componentPreviewImagesReady()
generateComponentPreviewImages(); generateComponentPreviewImages();
} }
void DocumentWindow::decorateComponentPreviewImages()
{
if (nullptr != m_componentPreviewImagesDecorator) {
m_isComponentPreviewImageDecorationsObsolete = true;
return;
}
m_isComponentPreviewImageDecorationsObsolete = false;
QThread *thread = new QThread;
auto previewInputs = std::make_unique<std::vector<ComponentPreviewImagesDecorator::PreviewInput>>();
for (auto &component: m_document->componentMap) {
if (!component.second.isPreviewImageDecorationObsolete)
continue;
component.second.isPreviewImageDecorationObsolete = false;
if (nullptr == component.second.previewImage)
continue;
previewInputs->emplace_back(ComponentPreviewImagesDecorator::PreviewInput {
component.first,
std::make_unique<QImage>(*component.second.previewImage),
!component.second.childrenIds.empty()
});
}
m_componentPreviewImagesDecorator = std::make_unique<ComponentPreviewImagesDecorator>(std::move(previewInputs));
m_componentPreviewImagesDecorator->moveToThread(thread);
connect(thread, &QThread::started, m_componentPreviewImagesDecorator.get(), &ComponentPreviewImagesDecorator::process);
connect(m_componentPreviewImagesDecorator.get(), &ComponentPreviewImagesDecorator::finished, this, &DocumentWindow::componentPreviewImageDecorationsReady);
connect(m_componentPreviewImagesDecorator.get(), &ComponentPreviewImagesDecorator::finished, thread, &QThread::quit);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
}
void DocumentWindow::componentPreviewImageDecorationsReady()
{
auto resultImages = m_componentPreviewImagesDecorator->takeResultImages();
if (nullptr != resultImages) {
for (auto &it: *resultImages) {
if (nullptr == it.second)
continue;
m_document->setComponentPreviewPixmap(it.first, QPixmap::fromImage(*it.second));
}
}
m_componentPreviewImagesDecorator.reset();
if (m_isComponentPreviewImageDecorationsObsolete)
decorateComponentPreviewImages();
}
ModelWidget *DocumentWindow::modelWidget() ModelWidget *DocumentWindow::modelWidget()
{ {
return m_modelRenderWidget; return m_modelRenderWidget;

View File

@ -1,6 +1,7 @@
#ifndef DUST3D_APPLICATION_DOCUMENT_WINDOW_H_ #ifndef DUST3D_APPLICATION_DOCUMENT_WINDOW_H_
#define DUST3D_APPLICATION_DOCUMENT_WINDOW_H_ #define DUST3D_APPLICATION_DOCUMENT_WINDOW_H_
#include <memory>
#include <QMainWindow> #include <QMainWindow>
#include <QShowEvent> #include <QShowEvent>
#include <QPushButton> #include <QPushButton>
@ -14,6 +15,7 @@
#include "model_widget.h" #include "model_widget.h"
#include "graphics_container_widget.h" #include "graphics_container_widget.h"
#include "mesh_preview_images_generator.h" #include "mesh_preview_images_generator.h"
#include "component_preview_images_decorator.h"
class Document; class Document;
class SkeletonGraphicsWidget; class SkeletonGraphicsWidget;
@ -82,6 +84,8 @@ public slots:
void toggleRotation(); void toggleRotation();
void generateComponentPreviewImages(); void generateComponentPreviewImages();
void componentPreviewImagesReady(); void componentPreviewImagesReady();
void decorateComponentPreviewImages();
void componentPreviewImageDecorationsReady();
void updateInprogressIndicator(); void updateInprogressIndicator();
void openRecentFile(); void openRecentFile();
void updateRecentFileActions(); void updateRecentFileActions();
@ -157,6 +161,9 @@ private:
MeshPreviewImagesGenerator *m_componentPreviewImagesGenerator = nullptr; MeshPreviewImagesGenerator *m_componentPreviewImagesGenerator = nullptr;
bool m_isComponentPreviewImagesObsolete = false; bool m_isComponentPreviewImagesObsolete = false;
std::unique_ptr<ComponentPreviewImagesDecorator> m_componentPreviewImagesDecorator;
bool m_isComponentPreviewImageDecorationsObsolete = false;
PartManageWidget *m_partManageWidget = nullptr; PartManageWidget *m_partManageWidget = nullptr;

View File

@ -1272,14 +1272,22 @@ void SkeletonDocument::setRadiusLockState(bool locked)
emit radiusLockStateChanged(); emit radiusLockStateChanged();
} }
void SkeletonDocument::setComponentPreviewImage(const dust3d::Uuid &componentId, const QImage &image) void SkeletonDocument::setComponentPreviewImage(const dust3d::Uuid &componentId, std::unique_ptr<QImage> image)
{ {
SkeletonComponent *component = (SkeletonComponent *)findComponent(componentId); SkeletonComponent *component = (SkeletonComponent *)findComponent(componentId);
if (nullptr == component) if (nullptr == component)
return; return;
component->isPreviewMeshObsolete = false; component->isPreviewImageDecorationObsolete = true;
component->previewPixmap = QPixmap::fromImage(image); component->previewImage = std::move(image);
emit componentPreviewImageChanged(componentId); }
void SkeletonDocument::setComponentPreviewPixmap(const dust3d::Uuid &componentId, const QPixmap &pixmap)
{
SkeletonComponent *component = (SkeletonComponent *)findComponent(componentId);
if (nullptr == component)
return;
component->previewPixmap = pixmap;
emit componentPreviewPixmapChanged(componentId);
} }
void SkeletonDocument::setComponentPreviewMesh(const dust3d::Uuid &componentId, std::unique_ptr<ModelMesh> mesh) void SkeletonDocument::setComponentPreviewMesh(const dust3d::Uuid &componentId, std::unique_ptr<ModelMesh> mesh)

View File

@ -427,6 +427,8 @@ public:
bool dirty = true; bool dirty = true;
std::vector<dust3d::Uuid> childrenIds; std::vector<dust3d::Uuid> childrenIds;
bool isPreviewMeshObsolete = false; bool isPreviewMeshObsolete = false;
std::unique_ptr<QImage> previewImage;
bool isPreviewImageDecorationObsolete = false;
QPixmap previewPixmap; QPixmap previewPixmap;
QString linkData() const QString linkData() const
{ {
@ -547,7 +549,7 @@ signals:
void componentAdded(dust3d::Uuid componentId); void componentAdded(dust3d::Uuid componentId);
void componentExpandStateChanged(dust3d::Uuid componentId); void componentExpandStateChanged(dust3d::Uuid componentId);
void componentPreviewMeshChanged(const dust3d::Uuid &componentId); void componentPreviewMeshChanged(const dust3d::Uuid &componentId);
void componentPreviewImageChanged(const dust3d::Uuid &componentId); void componentPreviewPixmapChanged(const dust3d::Uuid &componentId);
void nodeRemoved(dust3d::Uuid nodeId); void nodeRemoved(dust3d::Uuid nodeId);
void edgeRemoved(dust3d::Uuid edgeId); void edgeRemoved(dust3d::Uuid edgeId);
void nodeRadiusChanged(dust3d::Uuid nodeId); void nodeRadiusChanged(dust3d::Uuid nodeId);
@ -586,6 +588,7 @@ public:
void collectComponentDescendantParts(dust3d::Uuid componentId, std::vector<dust3d::Uuid> &partIds) const; void collectComponentDescendantParts(dust3d::Uuid componentId, std::vector<dust3d::Uuid> &partIds) const;
void collectComponentDescendantComponents(dust3d::Uuid componentId, std::vector<dust3d::Uuid> &componentIds) const; void collectComponentDescendantComponents(dust3d::Uuid componentId, std::vector<dust3d::Uuid> &componentIds) const;
void setComponentPreviewMesh(const dust3d::Uuid &componentId, std::unique_ptr<ModelMesh> mesh); void setComponentPreviewMesh(const dust3d::Uuid &componentId, std::unique_ptr<ModelMesh> mesh);
void setComponentPreviewImage(const dust3d::Uuid &componentId, std::unique_ptr<QImage> image);
void resetDirtyFlags(); void resetDirtyFlags();
void markAllDirty(); void markAllDirty();
@ -691,7 +694,7 @@ public slots:
void showDescendantComponents(dust3d::Uuid componentId); void showDescendantComponents(dust3d::Uuid componentId);
void lockDescendantComponents(dust3d::Uuid componentId); void lockDescendantComponents(dust3d::Uuid componentId);
void unlockDescendantComponents(dust3d::Uuid componentId); void unlockDescendantComponents(dust3d::Uuid componentId);
void setComponentPreviewImage(const dust3d::Uuid &componentId, const QImage &image); void setComponentPreviewPixmap(const dust3d::Uuid &componentId, const QPixmap &pixmap);
void setPartLockState(dust3d::Uuid partId, bool locked); void setPartLockState(dust3d::Uuid partId, bool locked);
void setPartVisibleState(dust3d::Uuid partId, bool visible); void setPartVisibleState(dust3d::Uuid partId, bool visible);
void setPartDisableState(dust3d::Uuid partId, bool disabled); void setPartDisableState(dust3d::Uuid partId, bool disabled);