diff --git a/application/application.pro b/application/application.pro index f39ed384..07e9e1fc 100644 --- a/application/application.pro +++ b/application/application.pro @@ -104,6 +104,8 @@ HEADERS += sources/component_list_model.h SOURCES += sources/component_list_model.cc HEADERS += sources/component_preview_grid_widget.h 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 SOURCES += sources/cut_face_preview.cc HEADERS += sources/dds_file.h diff --git a/application/sources/component_list_model.cc b/application/sources/component_list_model.cc index d5a0ab0a..35f9c413 100644 --- a/application/sources/component_list_model.cc +++ b/application/sources/component_list_model.cc @@ -7,7 +7,7 @@ ComponentListModel::ComponentListModel(const Document *document, QObject *parent QAbstractListModel(parent), 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 emit this->layoutChanged(); }); diff --git a/application/sources/component_preview_images_decorator.cc b/application/sources/component_preview_images_decorator.cc new file mode 100644 index 00000000..9478539f --- /dev/null +++ b/application/sources/component_preview_images_decorator.cc @@ -0,0 +1,44 @@ +#include +#include +#include "theme.h" +#include "component_preview_images_decorator.h" + +ComponentPreviewImagesDecorator::ComponentPreviewImagesDecorator(std::unique_ptr> previewInputs) +{ + m_previewInputs = std::move(previewInputs); +} + +std::unique_ptr>> ComponentPreviewImagesDecorator::takeResultImages() +{ + return std::move(m_resultImages); +} + +void ComponentPreviewImagesDecorator::decorate() +{ + if (nullptr == m_previewInputs) + return; + + m_resultImages = std::make_unique>>(); + + 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(); +} diff --git a/application/sources/component_preview_images_decorator.h b/application/sources/component_preview_images_decorator.h new file mode 100644 index 00000000..7de8179f --- /dev/null +++ b/application/sources/component_preview_images_decorator.h @@ -0,0 +1,32 @@ +#ifndef DUST3D_APPLICATION_COMPONENT_PREVIEW_IMAGES_DECORATOR_H_ +#define DUST3D_APPLICATION_COMPONENT_PREVIEW_IMAGES_DECORATOR_H_ + +#include +#include +#include +#include + +class ComponentPreviewImagesDecorator: public QObject +{ + Q_OBJECT +public: + struct PreviewInput + { + dust3d::Uuid id; + std::unique_ptr image; + bool isDirectory = false; + }; + + ComponentPreviewImagesDecorator(std::unique_ptr> previewInputs); + std::unique_ptr>> takeResultImages(); +signals: + void finished(); +public slots: + void process(); +private: + std::unique_ptr> m_previewInputs; + std::unique_ptr>> m_resultImages; + void decorate(); +}; + +#endif diff --git a/application/sources/document_window.cc b/application/sources/document_window.cc index 1ee66893..560dde4c 100644 --- a/application/sources/document_window.cc +++ b/application/sources/document_window.cc @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1197,9 +1198,10 @@ void DocumentWindow::generateComponentPreviewImages() QThread *thread = new QThread; 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) continue; + component.second.isPreviewMeshObsolete = false; m_componentPreviewImagesGenerator->addInput(component.first, std::unique_ptr(component.second.takePreviewMesh())); } m_componentPreviewImagesGenerator->moveToThread(thread); @@ -1216,9 +1218,11 @@ void DocumentWindow::componentPreviewImagesReady() componentImages.reset(m_componentPreviewImagesGenerator->takeImages()); if (nullptr != componentImages) { for (const auto &it: *componentImages) { - m_document->setComponentPreviewImage(it.first, it.second); + m_document->setComponentPreviewImage(it.first, std::make_unique(std::move(it.second))); } } + + decorateComponentPreviewImages(); delete m_componentPreviewImagesGenerator; m_componentPreviewImagesGenerator = nullptr; @@ -1227,6 +1231,56 @@ void DocumentWindow::componentPreviewImagesReady() generateComponentPreviewImages(); } +void DocumentWindow::decorateComponentPreviewImages() +{ + if (nullptr != m_componentPreviewImagesDecorator) { + m_isComponentPreviewImageDecorationsObsolete = true; + return; + } + + m_isComponentPreviewImageDecorationsObsolete = false; + + QThread *thread = new QThread; + + auto previewInputs = std::make_unique>(); + 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(*component.second.previewImage), + !component.second.childrenIds.empty() + }); + } + m_componentPreviewImagesDecorator = std::make_unique(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() { return m_modelRenderWidget; diff --git a/application/sources/document_window.h b/application/sources/document_window.h index 1fc8fa13..1376a437 100644 --- a/application/sources/document_window.h +++ b/application/sources/document_window.h @@ -1,6 +1,7 @@ #ifndef DUST3D_APPLICATION_DOCUMENT_WINDOW_H_ #define DUST3D_APPLICATION_DOCUMENT_WINDOW_H_ +#include #include #include #include @@ -14,6 +15,7 @@ #include "model_widget.h" #include "graphics_container_widget.h" #include "mesh_preview_images_generator.h" +#include "component_preview_images_decorator.h" class Document; class SkeletonGraphicsWidget; @@ -82,6 +84,8 @@ public slots: void toggleRotation(); void generateComponentPreviewImages(); void componentPreviewImagesReady(); + void decorateComponentPreviewImages(); + void componentPreviewImageDecorationsReady(); void updateInprogressIndicator(); void openRecentFile(); void updateRecentFileActions(); @@ -157,6 +161,9 @@ private: MeshPreviewImagesGenerator *m_componentPreviewImagesGenerator = nullptr; bool m_isComponentPreviewImagesObsolete = false; + + std::unique_ptr m_componentPreviewImagesDecorator; + bool m_isComponentPreviewImageDecorationsObsolete = false; PartManageWidget *m_partManageWidget = nullptr; diff --git a/application/sources/skeleton_document.cc b/application/sources/skeleton_document.cc index 678dc989..c60f7b7f 100644 --- a/application/sources/skeleton_document.cc +++ b/application/sources/skeleton_document.cc @@ -1272,14 +1272,22 @@ void SkeletonDocument::setRadiusLockState(bool locked) emit radiusLockStateChanged(); } -void SkeletonDocument::setComponentPreviewImage(const dust3d::Uuid &componentId, const QImage &image) +void SkeletonDocument::setComponentPreviewImage(const dust3d::Uuid &componentId, std::unique_ptr image) { SkeletonComponent *component = (SkeletonComponent *)findComponent(componentId); if (nullptr == component) return; - component->isPreviewMeshObsolete = false; - component->previewPixmap = QPixmap::fromImage(image); - emit componentPreviewImageChanged(componentId); + component->isPreviewImageDecorationObsolete = true; + component->previewImage = std::move(image); +} + +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 mesh) diff --git a/application/sources/skeleton_document.h b/application/sources/skeleton_document.h index bc699a01..98f086cf 100644 --- a/application/sources/skeleton_document.h +++ b/application/sources/skeleton_document.h @@ -427,6 +427,8 @@ public: bool dirty = true; std::vector childrenIds; bool isPreviewMeshObsolete = false; + std::unique_ptr previewImage; + bool isPreviewImageDecorationObsolete = false; QPixmap previewPixmap; QString linkData() const { @@ -547,7 +549,7 @@ signals: void componentAdded(dust3d::Uuid componentId); void componentExpandStateChanged(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 edgeRemoved(dust3d::Uuid edgeId); void nodeRadiusChanged(dust3d::Uuid nodeId); @@ -586,6 +588,7 @@ public: void collectComponentDescendantParts(dust3d::Uuid componentId, std::vector &partIds) const; void collectComponentDescendantComponents(dust3d::Uuid componentId, std::vector &componentIds) const; void setComponentPreviewMesh(const dust3d::Uuid &componentId, std::unique_ptr mesh); + void setComponentPreviewImage(const dust3d::Uuid &componentId, std::unique_ptr image); void resetDirtyFlags(); void markAllDirty(); @@ -691,7 +694,7 @@ public slots: void showDescendantComponents(dust3d::Uuid componentId); void lockDescendantComponents(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 setPartVisibleState(dust3d::Uuid partId, bool visible); void setPartDisableState(dust3d::Uuid partId, bool disabled);