添加导出PNG的功能

main
wuyize 2023-04-25 14:49:26 +08:00
parent fbc08b30fb
commit dad2c1fa59
13 changed files with 372 additions and 138 deletions

View File

@ -162,6 +162,7 @@
<ClCompile Include="src\Renderer\Painting\Painting.cpp" /> <ClCompile Include="src\Renderer\Painting\Painting.cpp" />
<ClCompile Include="src\Renderer\PaintingMesh.cpp" /> <ClCompile Include="src\Renderer\PaintingMesh.cpp" />
<ClCompile Include="src\Renderer\Preview\ElementRenderer.cpp" /> <ClCompile Include="src\Renderer\Preview\ElementRenderer.cpp" />
<ClCompile Include="src\Renderer\Preview\PaintingRenderer.cpp" />
<ClCompile Include="src\Renderer\RendererGLWidget.cpp" /> <ClCompile Include="src\Renderer\RendererGLWidget.cpp" />
<ClCompile Include="src\Renderer\RendererWidget.cpp" /> <ClCompile Include="src\Renderer\RendererWidget.cpp" />
<ClCompile Include="src\Renderer\Painting\ShortCutTree.cpp" /> <ClCompile Include="src\Renderer\Painting\ShortCutTree.cpp" />
@ -253,6 +254,7 @@
<ClInclude Include="src\Renderer\Painting\MaterialStyleStroke.h" /> <ClInclude Include="src\Renderer\Painting\MaterialStyleStroke.h" />
<ClInclude Include="src\Renderer\Painting\Painting.h" /> <ClInclude Include="src\Renderer\Painting\Painting.h" />
<ClInclude Include="src\Renderer\Preview\ElementRenderer.h" /> <ClInclude Include="src\Renderer\Preview\ElementRenderer.h" />
<ClInclude Include="src\Renderer\Preview\PaintingRenderer.h" />
<ClInclude Include="src\Renderer\VirtualTextureManager.h" /> <ClInclude Include="src\Renderer\VirtualTextureManager.h" />
<ClInclude Include="src\SvgParser.h" /> <ClInclude Include="src\SvgParser.h" />
<QtMoc Include="src\NavigationBarWidget.h" /> <QtMoc Include="src\NavigationBarWidget.h" />

View File

@ -282,6 +282,9 @@
<ClCompile Include="src\Editor\Properties\ProjectPropertyWidget.cpp"> <ClCompile Include="src\Editor\Properties\ProjectPropertyWidget.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="src\Renderer\Preview\PaintingRenderer.cpp">
<Filter>Source Files\Renderer\Preview</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<QtMoc Include="src\Renderer\RendererGLWidget.h"> <QtMoc Include="src\Renderer\RendererGLWidget.h">
@ -570,6 +573,9 @@
<ClInclude Include="resource.h"> <ClInclude Include="resource.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="src\Renderer\Preview\PaintingRenderer.h">
<Filter>Header Files\Renderer\Preview</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<QtRcc Include="res\MainWindow.qrc"> <QtRcc Include="res\MainWindow.qrc">

View File

@ -69,10 +69,12 @@ void EditorWidget::initFileMenu()
auto* actionOpen = new QAction(QStringLiteral("´ò¿ª"), fileMenuButton); auto* actionOpen = new QAction(QStringLiteral("´ò¿ª"), fileMenuButton);
auto* actionSave = new QAction(QStringLiteral("±£´æ"), fileMenuButton); auto* actionSave = new QAction(QStringLiteral("±£´æ"), fileMenuButton);
auto* actionSaveAs = new QAction(QStringLiteral("Áí´æΪ"), fileMenuButton); auto* actionSaveAs = new QAction(QStringLiteral("Áí´æΪ"), fileMenuButton);
auto* actionExport = new QAction(QStringLiteral("µ¼³ö"), fileMenuButton);
fileMenuButton->addMenuAction(actionCreate); fileMenuButton->addMenuAction(actionCreate);
fileMenuButton->addMenuAction(actionOpen); fileMenuButton->addMenuAction(actionOpen);
fileMenuButton->addMenuAction(actionSave); fileMenuButton->addMenuAction(actionSave);
fileMenuButton->addMenuAction(actionSaveAs); fileMenuButton->addMenuAction(actionSaveAs);
fileMenuButton->addMenuAction(actionExport);
connect(actionCreate, &QAction::triggered, [this] { connect(actionCreate, &QAction::triggered, [this] {
static int count = 0; static int count = 0;
const int prevCount = this->tabWidget->count(); const int prevCount = this->tabWidget->count();
@ -108,6 +110,13 @@ void EditorWidget::initFileMenu()
item->saveAs(fileName); item->saveAs(fileName);
} }
}); });
connect(actionExport, &QAction::triggered, this, [this] {
auto* item = dynamic_cast<EditorWidgetItem*>(this->tabWidget->currentWidget());
if (item != nullptr)
{
item->exportAs();
}
});
} }
void EditorWidget::initProjectMenu() void EditorWidget::initProjectMenu()

View File

@ -1,126 +1,131 @@
#include "EditorWidgetItem.h" #include "EditorWidgetItem.h"
#include "EditorWidget.h" #include "EditorWidget.h"
#include "DataManager/ProjectDataManager.h" #include "DataManager/ProjectDataManager.h"
#include "../Renderer/Preview/PaintingRenderer.h"
#include "util/PaintingUtil.h"
#include <QTimer> #include <QTimer>
#include <QFileInfo> #include <QFileInfo>
#include <QFile> #include <QFile>
#include <QDir> #include <QDir>
#include <QDialog>
#include <QDialogButtonBox>
#include <QFileDialog>
EditorWidgetItem::EditorWidgetItem(QString filePath,QWidget *parent) : QWidget(parent) EditorWidgetItem::EditorWidgetItem(QString filePath, QWidget* parent) : QWidget(parent)
{ {
QImage x; QImage x;
this->parent = parent; this->parent = parent;
displayLayer = nullptr; displayLayer = nullptr;
displayElement = nullptr; displayElement = nullptr;
ui.setupUi(this); ui.setupUi(this);
previewWindow = ui.Preview; previewWindow = ui.Preview;
treeWidget = ui.LayerTree; treeWidget = ui.LayerTree;
this->filePath = filePath; this->filePath = filePath;
elementInfoDisplayWidget = ui.ElementDisplay; elementInfoDisplayWidget = ui.ElementDisplay;
layerInfoDisplayWidget = ui.LayerDisplay; layerInfoDisplayWidget = ui.LayerDisplay;
elementInfoDisplayWidget->enableEdit(); elementInfoDisplayWidget->enableEdit();
qDebug() << layerInfoDisplayWidget; qDebug() << layerInfoDisplayWidget;
qDebug() << elementInfoDisplayWidget; qDebug() << elementInfoDisplayWidget;
auto centralRefresh = [this]() { auto centralRefresh = [this]() {
layerInfoDisplayWidget->refresh(); layerInfoDisplayWidget->refresh();
treeWidget->refresh(); treeWidget->refresh();
previewWindow->refresh(); previewWindow->refresh();
elementInfoDisplayWidget->lazyRefresh(); elementInfoDisplayWidget->lazyRefresh();
}; };
connect(previewWindow, &PreviewWindow::triggerCentralRefresh, centralRefresh); connect(previewWindow, &PreviewWindow::triggerCentralRefresh, centralRefresh);
connect(layerInfoDisplayWidget, &InfoDisplayWidget::triggerCentralRefresh, centralRefresh); connect(layerInfoDisplayWidget, &InfoDisplayWidget::triggerCentralRefresh, centralRefresh);
connect(elementInfoDisplayWidget, &ElementPoolWidget::triggerCentralRefresh, centralRefresh); connect(elementInfoDisplayWidget, &ElementPoolWidget::triggerCentralRefresh, centralRefresh);
connect(treeWidget, &LayerTreeWidget::triggerCentralRefresh, centralRefresh); connect(treeWidget, &LayerTreeWidget::triggerCentralRefresh, centralRefresh);
connect(previewWindow, &PreviewWindow::refreshElementPreviewByIndex, elementInfoDisplayWidget, &ElementPoolWidget::refreshPictureByIndex); connect(previewWindow, &PreviewWindow::refreshElementPreviewByIndex, elementInfoDisplayWidget, &ElementPoolWidget::refreshPictureByIndex);
connect(treeWidget, &LayerTreeWidget::displayLayerChange, previewWindow, &PreviewWindow::currentLayerChanged); connect(treeWidget, &LayerTreeWidget::displayLayerChange, previewWindow, &PreviewWindow::currentLayerChanged);
//connect(treeWidget, &LayerTreeWidget::requireRefreshElementWidget, elementInfoDisplayWidget, &ElementPoolWidget::refresh); //connect(treeWidget, &LayerTreeWidget::requireRefreshElementWidget, elementInfoDisplayWidget, &ElementPoolWidget::refresh);
// connect(layerInfoDisplayWidget, &InfoDisplayWidget::requireRefreshElementWidget, elementInfoDisplayWidget, &ElementPoolWidget::refresh); // connect(layerInfoDisplayWidget, &InfoDisplayWidget::requireRefreshElementWidget, elementInfoDisplayWidget, &ElementPoolWidget::refresh);
connect(treeWidget, &LayerTreeWidget::displayLayerChange, this, &EditorWidgetItem::onLayerChange); connect(treeWidget, &LayerTreeWidget::displayLayerChange, this, &EditorWidgetItem::onLayerChange);
// connect(layerInfoDisplayWidget, &InfoDisplayWidget::requireRefreshPreview, this, // connect(layerInfoDisplayWidget, &InfoDisplayWidget::requireRefreshPreview, this,
// &EditorWidgetItem::triggerRefreshPreview); // &EditorWidgetItem::triggerRefreshPreview);
// connect(treeWidget, &LayerTreeWidget::requireRefreshPreview, this, // connect(treeWidget, &LayerTreeWidget::requireRefreshPreview, this,
// &EditorWidgetItem::triggerRefreshPreview); // &EditorWidgetItem::triggerRefreshPreview);
//connect(layerInfoDisplayWidget, &InfoDisplayWidget::requireSelfRefresh, layerInfoDisplayWidget, &InfoDisplayWidget::triggerSelfRefresh); //connect(layerInfoDisplayWidget, &InfoDisplayWidget::requireSelfRefresh, layerInfoDisplayWidget, &InfoDisplayWidget::triggerSelfRefresh);
// connect(elementInfoDisplayWidget, &ElementPoolWidget::refreshLayerTree, treeWidget, &LayerTreeWidget::refresh); // connect(elementInfoDisplayWidget, &ElementPoolWidget::refreshLayerTree, treeWidget, &LayerTreeWidget::refresh);
// &EditorWidget::triggerRefreshPreview); // &EditorWidget::triggerRefreshPreview);
// test // test
QFile settingFile; QFile settingFile;
//settingFile.setFileName("../data.json"); //settingFile.setFileName("../data.json");
settingFile.setFileName(filePath); settingFile.setFileName(filePath);
settingFile.open(QFile::ReadOnly); settingFile.open(QFile::ReadOnly);
QByteArray setting = settingFile.readAll().trimmed(); QByteArray setting = settingFile.readAll().trimmed();
QJsonParseError jError; QJsonParseError jError;
QJsonDocument jsonDoc(QJsonDocument::fromJson(setting, &jError)); QJsonDocument jsonDoc(QJsonDocument::fromJson(setting, &jError));
qDebug() << jsonDoc.object().value("height").toDouble(); qDebug() << jsonDoc.object().value("height").toDouble();
qDebug() << jError.errorString(); qDebug() << jError.errorString();
ProjectData data; ProjectData data;
data.fileHome = QFileInfo(filePath).absolutePath(); data.fileHome = QFileInfo(filePath).absolutePath();
data.width = jsonDoc.object().value("height").toInt(); data.width = jsonDoc.object().value("height").toInt();
data.height = jsonDoc.object().value("width").toInt(); data.height = jsonDoc.object().value("width").toInt();
data.zoomX = 1.0; data.zoomX = 1.0;
data.zoomY = 1.0; data.zoomY = 1.0;
data.item = this; data.item = this;
ProjectDataManager::Instance()->addProjectData(data); ProjectDataManager::Instance()->addProjectData(data);
// end test // end test
QJsonObject source = jsonDoc.object(); QJsonObject source = jsonDoc.object();
elementManager = new ElementManager(source, QFileInfo(filePath).absolutePath()); elementManager = new ElementManager(source, QFileInfo(filePath).absolutePath());
layerManager = new LayerManager(source, elementManager); layerManager = new LayerManager(source, elementManager);
elementInfoDisplayWidget->setElementManager(elementManager); elementInfoDisplayWidget->setElementManager(elementManager);
treeWidget->elementManager = elementManager; treeWidget->elementManager = elementManager;
//qDebug() << layerManager->toJson(); //qDebug() << layerManager->toJson();
previewWindow->initialize(layerManager,QSize(jsonDoc.object().value("width").toInt(),jsonDoc.object().value("height").toInt())); previewWindow->initialize(layerManager, QSize(jsonDoc.object().value("width").toInt(), jsonDoc.object().value("height").toInt()));
if (source.contains("metallic")) if (source.contains("metallic"))
previewWindow->canvasMetallic = jsonDoc.object().value("metallic").toDouble(); previewWindow->canvasMetallic = jsonDoc.object().value("metallic").toDouble();
else else
previewWindow->canvasMetallic = 0; previewWindow->canvasMetallic = 0;
if (source.contains("roughness")) if (source.contains("roughness"))
previewWindow->canvasRoughness = jsonDoc.object().value("roughness").toDouble(); previewWindow->canvasRoughness = jsonDoc.object().value("roughness").toDouble();
else else
previewWindow->canvasRoughness = 0.5; previewWindow->canvasRoughness = 0.5;
if (layerManager->getRoot() != nullptr) if (layerManager->getRoot() != nullptr)
{ {
treeWidget->root = layerManager->getRoot(); treeWidget->root = layerManager->getRoot();
treeWidget->refresh(); treeWidget->refresh();
treeWidget->addTopLevelItem(treeWidget->root->getQTreeItem()); treeWidget->addTopLevelItem(treeWidget->root->getQTreeItem());
} }
this->backgroundColor = source.value("background-color").toVariant().value<QColor>(); this->backgroundColor = source.value("background-color").toVariant().value<QColor>();
this->projectName = source.value("project-name").toString(); this->projectName = source.value("project-name").toString();
qDebug() << this->backgroundColor; qDebug() << this->backgroundColor;
qDebug() << this->projectName; qDebug() << this->projectName;
elementInfoDisplayWidget->refresh(); elementInfoDisplayWidget->refresh();
QTimer::singleShot(300, this, [this, centralRefresh]() { QTimer::singleShot(300, this, [this, centralRefresh]() {
handleBackgroundColorChange(this->backgroundColor); handleBackgroundColorChange(this->backgroundColor);
handleProjectNameChange(this->projectName); handleProjectNameChange(this->projectName);
centralRefresh(); centralRefresh();
}); });
} }
EditorWidgetItem::~EditorWidgetItem() EditorWidgetItem::~EditorWidgetItem()
{ {
} }
void EditorWidgetItem::paintEvent(QPaintEvent *event) void EditorWidgetItem::paintEvent(QPaintEvent* event)
{ {
QPainter painter(this); QPainter painter(this);
// 设置画刷的颜色为灰色,并填充整个窗口区域 // 设置画刷的颜色为灰色,并填充整个窗口区域
painter.setBrush(Qt::gray); painter.setBrush(Qt::gray);
painter.drawRect(this->rect()); painter.drawRect(this->rect());
} }
void EditorWidgetItem::onLayerChange(LayerWrapper *layer) void EditorWidgetItem::onLayerChange(LayerWrapper* layer)
{ {
displayLayer = layer; displayLayer = layer;
// TODO : notify InfoDisplayWidget and update // TODO : notify InfoDisplayWidget and update
ui.LayerDisplay->setLayer(layer); ui.LayerDisplay->setLayer(layer);
this->update(); this->update();
} }
void EditorWidgetItem::triggerRefreshPreview() void EditorWidgetItem::triggerRefreshPreview()
{ {
previewWindow->update(); previewWindow->update();
} }
void EditorWidgetItem::save() const void EditorWidgetItem::save() const
@ -132,36 +137,103 @@ void EditorWidgetItem::saveAs(QString filePath) const
{ {
saveImpl(filePath); saveImpl(filePath);
QString srcHome = QFileInfo(this->filePath).absolutePath(); QString srcHome = QFileInfo(this->filePath).absolutePath();
if (!QDir(QFileInfo(filePath).absolutePath() + "/svg").exists()) if (!QDir(QFileInfo(filePath).absolutePath() + "/svg").exists())
{ {
QDir().mkdir(QFileInfo(filePath).absolutePath() + "/svg"); QDir().mkdir(QFileInfo(filePath).absolutePath() + "/svg");
} }
for (auto& ele : elementManager->elements) for (auto& ele : elementManager->elements)
{ {
auto e = dynamic_cast<SimpleElement*>(ele); auto e = dynamic_cast<SimpleElement*>(ele);
if (e != nullptr) if (e != nullptr)
{ {
QString fileName = e->jsonSource["data"].toObject()["include"].toString(); QString fileName = e->jsonSource["data"].toObject()["include"].toString();
QString src = srcHome + fileName; QString src = srcHome + fileName;
QString dst = QFileInfo(filePath).absolutePath() + fileName; QString dst = QFileInfo(filePath).absolutePath() + fileName;
QFile::copy(src, dst); QFile::copy(src, dst);
} }
} }
} }
void EditorWidgetItem::exportAs()
{
const QString fileName = QFileDialog::getSaveFileName(this, QStringLiteral("导出"), QFileInfo(filePath).dir().filePath(projectName), QStringLiteral("PNG图像(*.png)"));
if (fileName.isEmpty())
return;
QtMaterialTextField widthTextField;
QtMaterialTextField heightTextField;
QtMaterialTextField supersampleTextField;
widthTextField.setLabel(QStringLiteral("宽度(像素)"));
heightTextField.setLabel(QStringLiteral("高度(像素)"));
supersampleTextField.setLabel(QStringLiteral("超采样(倍)"));
class FixedIntValidator : public QIntValidator
{
public:
explicit FixedIntValidator(QObject* parent = nullptr) : QIntValidator(parent) {}
explicit FixedIntValidator(int bottom, int top, QObject* parent = nullptr) : QIntValidator(
bottom, top, parent) {}
void fixup(QString& s) const override
{
s = QString::number(bottom());
}
};
widthTextField.setValidator(new FixedIntValidator(1, std::numeric_limits<int>::max(), &widthTextField));
heightTextField.setValidator(new FixedIntValidator(1, std::numeric_limits<int>::max(), &heightTextField));
supersampleTextField.setValidator(new QDoubleValidator(0.125, 128, 3));
widthTextField.setText(QString::number(previewWindow->referSize.width()));
heightTextField.setText(QString::number(previewWindow->referSize.height()));
supersampleTextField.setText(QString::number(2));
connect(&widthTextField, &QLineEdit::textChanged, [&](const QString& string) {
heightTextField.setText(QString::number(previewWindow->referSize.height() * string.toInt() / (double)previewWindow->referSize.width()));
});
connect(&heightTextField, &QLineEdit::textChanged, [&](const QString& string) {
widthTextField.setText(QString::number(previewWindow->referSize.width() * string.toInt() / (double)previewWindow->referSize.height()));
});
QDialog* dialog = new QDialog(this);
dialog->setMinimumSize(QSize(200, 200));
dialog->setWindowTitle(QStringLiteral("导出"));
dialog->setWindowFlag(Qt::WindowContextHelpButtonHint, false);
auto layout = new QVBoxLayout(dialog);
dialog->setLayout(layout);
layout->addWidget(&widthTextField);
layout->addWidget(&heightTextField);
layout->addWidget(&supersampleTextField);
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, dialog);
buttonBox->button(QDialogButtonBox::Ok)->setText(QStringLiteral("确定"));
buttonBox->button(QDialogButtonBox::Cancel)->setText(QStringLiteral("取消"));
layout->addWidget(buttonBox);
connect(buttonBox, &QDialogButtonBox::accepted, [&] {
dialog->setCursor(Qt::WaitCursor);
auto painting = PaintingUtil::transfromToPainting(this->filePath);
Renderer::PaintingRenderer::instance().exportPainting(fileName, painting, QSize(widthTextField.text().toInt(), heightTextField.text().toInt()), supersampleTextField.text().toFloat());
dialog->unsetCursor();
dialog->accept();
});
connect(buttonBox, &QDialogButtonBox::rejected, [&] {
dialog->close();
});
dialog->exec();
}
void EditorWidgetItem::saveImpl(QString filePath) const void EditorWidgetItem::saveImpl(QString filePath) const
{ {
QJsonObject source1 = layerManager->toJson(); QJsonObject source1 = layerManager->toJson();
QJsonObject source2 = elementManager->toJson(); QJsonObject source2 = elementManager->toJson();
QJsonObject json; QJsonObject json;
json.insert("roughness", previewWindow->canvasRoughness); json.insert("roughness", previewWindow->canvasRoughness);
json.insert("metallic", previewWindow->canvasMetallic); json.insert("metallic", previewWindow->canvasMetallic);
json.insert("width", previewWindow->referSize.width()); json.insert("width", previewWindow->referSize.width());
json.insert("height", previewWindow->referSize.height()); json.insert("height", previewWindow->referSize.height());
json.insert("root-layer", source1.value("root-layer")); json.insert("root-layer", source1.value("root-layer"));
json.insert("elements", source2.value("elements")); json.insert("elements", source2.value("elements"));
json.insert("project-name", this->projectName); json.insert("project-name", this->projectName);
json.insert("background-color", QJsonValue::fromVariant(QVariant::fromValue(this->backgroundColor))); json.insert("background-color", QJsonValue::fromVariant(QVariant::fromValue(this->backgroundColor)));
QJsonDocument doc(json); QJsonDocument doc(json);
QFile file(filePath); QFile file(filePath);
@ -179,17 +251,17 @@ void EditorWidgetItem::handleBackgroundColorChange(const QColor& color)
void EditorWidgetItem::handleProjectNameChange(const QString& name) void EditorWidgetItem::handleProjectNameChange(const QString& name)
{ {
this->projectName = name; this->projectName = name;
auto parent = dynamic_cast<EditorWidget*>(this->parent); auto parent = dynamic_cast<EditorWidget*>(this->parent);
qDebug() << name << " " << parent<<" "<<this; qDebug() << name << " " << parent << " " << this;
if (parent != nullptr) if (parent != nullptr)
{ {
parent->renameTab(this, name); parent->renameTab(this, name);
} }
} }
void EditorWidgetItem::handleCanvasSizeChange(const QSize& size) void EditorWidgetItem::handleCanvasSizeChange(const QSize& size)
{ {
previewWindow->resize(size); previewWindow->resize(size);
} }
void EditorWidgetItem::handleCanvasRoughnessChange(const float& roughness) void EditorWidgetItem::handleCanvasRoughnessChange(const float& roughness)
@ -204,7 +276,7 @@ void EditorWidgetItem::handleCanvasMetallicChange(const float& metallic)
QSize EditorWidgetItem::getCanvasReferSize() const QSize EditorWidgetItem::getCanvasReferSize() const
{ {
return previewWindow->referSize; return previewWindow->referSize;
} }
float EditorWidgetItem::getCanvasRoughness() const float EditorWidgetItem::getCanvasRoughness() const

View File

@ -32,7 +32,7 @@ class EditorWidgetItem : public QWidget
GraphicElement *displayElement; GraphicElement *displayElement;
QWidget* parent; QWidget* parent;
void saveImpl(QString filePath)const; void saveImpl(QString filePath)const;
public: public:
// PROJECT INFO // PROJECT INFO
QString filePath; QString filePath;
QString projectName; QString projectName;
@ -43,7 +43,8 @@ public:
~EditorWidgetItem(); ~EditorWidgetItem();
void paintEvent(QPaintEvent *event) override; void paintEvent(QPaintEvent *event) override;
void save() const; void save() const;
void saveAs(QString filePath)const; void saveAs(QString filePath) const;
void exportAs();
void handleBackgroundColorChange(const QColor& color); void handleBackgroundColorChange(const QColor& color);
void handleProjectNameChange(const QString& name); void handleProjectNameChange(const QString& name);
void handleCanvasSizeChange(const QSize& size); void handleCanvasSizeChange(const QSize& size);

View File

@ -0,0 +1,82 @@
#include "PaintingRenderer.h"
#include <QOpenGLFramebufferObject>
Renderer::PaintingRenderer& Renderer::PaintingRenderer::instance()
{
static PaintingRenderer renderer;
return renderer;
}
bool Renderer::PaintingRenderer::exportPainting(const QString& path, Painting& painting, const QSize& size, float supersampling)
{
std::unique_lock<std::mutex> lock(drawMutex);
draw.wait(lock, [&] {return drawFinished; });
drawFinished = false;
this->path = path;
this->painting = &painting;
this->size = size;
this->supersampling = supersampling;
needDraw = true;
draw.notify_all();
draw.wait(lock, [&] {return drawFinished; });
return true;
}
Renderer::PaintingRenderer::PaintingRenderer()
{
surface.create();
thread = std::jthread([&](std::stop_token stop) {
std::stop_callback cb(stop, [&] {
draw.notify_all();
});
QOpenGLContext context;
context.create();
context.makeCurrent(&surface);
auto gl = context.versionFunctions<QOpenGLFunctions_4_5_Core>();
shader = std::make_unique<QOpenGLShaderProgram>();
if (!shader->addShaderFromSourceFile(QOpenGLShader::Compute, ":/Shaders/painting.comp"))
qDebug() << "ERROR: " << shader->log();
if (!shader->link())
qDebug() << "ERROR: " << shader->log();
initialized = true;
while (!stop.stop_requested())
{
std::unique_lock<std::mutex> lock(drawMutex);
draw.wait(lock, [&] {return needDraw || stop.stop_requested(); });
if (needDraw)
{
needDraw = false;
painting->generateBuffers(gl);
QSize renderSize = size * supersampling;
auto fbo = QOpenGLFramebufferObject(renderSize, QOpenGLFramebufferObject::NoAttachment, GL_TEXTURE_2D);
fbo.bind();
gl->glClearColor(0, 0, 0, 0);
gl->glClear(GL_COLOR_BUFFER_BIT);
fbo.release();
shader->bind();
for (int i = 0; i < 6; i++)
gl->glBindBufferBase(GL_SHADER_STORAGE_BUFFER, i, painting->buffers[i]);
gl->glBindBufferBase(GL_UNIFORM_BUFFER, 1, painting->buffers[6]);
gl->glUniform2i(gl->glGetUniformLocation(shader->programId(), "pixelOffset"), 0, 0);
gl->glBindImageTexture(0, fbo.texture(), 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8);
gl->glBindImageTexture(1, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8);
gl->glDispatchCompute((GLuint)ceil(renderSize.width() / 8.), (GLuint)ceil(renderSize.height() / 8.), 1);
shader->release();
auto image = fbo.toImage(true);
image.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation).save(path, "PNG", 100);
gl->glDeleteBuffers(7, painting->buffers.data());
drawFinished = true;
}
draw.notify_all();
}
context.doneCurrent();
});
while (!initialized)
std::this_thread::yield();
}

View File

@ -0,0 +1,35 @@
#pragma once
#include <QOpenGLWidget>
#include <QOpenGLFunctions_4_5_Core>
#include <QOPenGLShaderProgram>
#include "../Painting/Painting.h"
#include <QOffscreenSurface>
namespace Renderer
{
class PaintingRenderer
{
public:
static PaintingRenderer& instance();
bool exportPainting(const QString& path, Painting& painting, const QSize& size, float supersampling = 2.f);
protected:
std::unique_ptr<QOpenGLShaderProgram> shader;
std::jthread thread;
QOffscreenSurface surface;
QOpenGLContext context;
std::atomic<bool> initialized = false;
bool drawFinished = true;
bool needDraw = false;
std::condition_variable draw;
std::mutex drawMutex;
Painting* painting;
QString path;
QSize size;
float supersampling;
std::pair<QImage, QPointF> result;
PaintingRenderer();
};
}

View File

@ -211,7 +211,7 @@ void Renderer::VirtualTextureManager::pageCommitmentById(const glm::u16vec2& pag
program.bind(); program.bind();
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
gl->BindBufferBase(GL_SHADER_STORAGE_BUFFER, i, painting.buffers[i]); gl->BindBufferBase(GL_SHADER_STORAGE_BUFFER, i, painting.buffers[i]);
glMain->BindBufferBase(GL_UNIFORM_BUFFER, 1, painting.buffers[6]); gl->BindBufferBase(GL_UNIFORM_BUFFER, 1, painting.buffers[6]);
gl->Uniform2i(gl->GetUniformLocation(program.programId(), "pixelOffset"), static_cast<GLsizei>(pageSize) * page.x, static_cast<GLsizei>(pageSize) * page.y); gl->Uniform2i(gl->GetUniformLocation(program.programId(), "pixelOffset"), static_cast<GLsizei>(pageSize) * page.x, static_cast<GLsizei>(pageSize) * page.y);
gl->BindImageTexture(0, painting.baseColor, level, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8); gl->BindImageTexture(0, painting.baseColor, level, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8);
gl->BindImageTexture(1, painting.metallicRoughness, level, GL_FALSE, 0, GL_READ_WRITE, GL_RG8); gl->BindImageTexture(1, painting.metallicRoughness, level, GL_FALSE, 0, GL_READ_WRITE, GL_RG8);

36
UnitTest/EPaperTest.cpp Normal file
View File

@ -0,0 +1,36 @@
#include "EPaperTest.h"
#include "CppUnitTest.h"
#include <QApplication>
#include <QWidget>
#include <qlabel.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTest
{
TEST_CLASS(EPaperTest)
{
private:
char* argv[1];
int argc;
public:
EPaperTest() :argv{ (char*)"" }, argc(1) {}
TEST_METHOD(TestBothSidesRound)
{
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
QApplication a(argc, argv);
QLabel w;
w.setFixedSize({296,128});
QImage image(QSize(296, 128), QImage::Format_RGB32);
image.fill(Qt::red);
w.setPixmap(QPixmap::fromImage(image));
w.show();
a.exec();
}
};
}

1
UnitTest/EPaperTest.h Normal file
View File

@ -0,0 +1 @@
#pragma once

View File

@ -3,6 +3,8 @@
#include <QApplication> #include <QApplication>
#include "Renderer/Painting/Painting.h" #include "Renderer/Painting/Painting.h"
#include "Renderer/Painting/MaterialStyleStroke.h" #include "Renderer/Painting/MaterialStyleStroke.h"
#include "Renderer/Preview/PaintingRenderer.h"
#include <util/PaintingUtil.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework; using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace Renderer; using namespace Renderer;
@ -17,35 +19,11 @@ namespace UnitTest
public: public:
PaintingTest() :argv{ (char*)"" }, argc(1) {} PaintingTest() :argv{ (char*)"" }, argc(1) {}
TEST_METHOD_INITIALIZE(initialize)
{
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
}
TEST_METHOD(TestBothSidesRound) TEST_METHOD(TestBothSidesRound)
{ {
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication app(argc, argv);
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); auto painting = PaintingUtil::transfromToPainting("E:\\3D Objects\\dougong\\paintings\\Test1\\Test1.json");
QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); PaintingRenderer::instance().exportPainting("E:\\3D Objects\\dougong\\paintings\\Test1\\Test1.png", painting, QSize(5910, 10940));
QApplication a(argc, argv);
class StyleStrokeRadialGradient : public Renderer::ElementStyle
{
virtual std::vector<Renderer::BaseStyle> toBaseStyles() const override
{
std::map<float, Material> materialMap = {
{0.20, Material{QColor(255,255,255)}},
{0.60, Material{QColor(165,176,207)}},
{1.00, Material{QColor(58,64,151)}}
};
return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleStroke>(60, StrokeType::kBothSides, StrokeEndType::kRound,
std::make_shared<StrokeRadialGradient>(materialMap, false))) };
}
} style;
TestPaintingGLWidget w(style);
w.show();
a.exec();
} }
}; };

View File

@ -111,6 +111,7 @@
<DynamicSource Condition="'$(Configuration)|$(Platform)'=='Release|x64'">input</DynamicSource> <DynamicSource Condition="'$(Configuration)|$(Platform)'=='Release|x64'">input</DynamicSource>
<QtMocFileName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(Filename).moc</QtMocFileName> <QtMocFileName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(Filename).moc</QtMocFileName>
</ClCompile> </ClCompile>
<ClCompile Include="EPaperTest.cpp" />
<ClCompile Include="LayerStyleTest.cpp" /> <ClCompile Include="LayerStyleTest.cpp" />
<ClCompile Include="PaintingTest.cpp"> <ClCompile Include="PaintingTest.cpp">
<DynamicSource Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">input</DynamicSource> <DynamicSource Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">input</DynamicSource>
@ -136,6 +137,9 @@
<ItemGroup> <ItemGroup>
<QtMoc Include="PaintingTest.h" /> <QtMoc Include="PaintingTest.h" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ClInclude Include="EPaperTest.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')"> <ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')">
<Import Project="$(QtMsBuild)\qt.targets" /> <Import Project="$(QtMsBuild)\qt.targets" />

View File

@ -54,5 +54,13 @@
<ClCompile Include="LayerStyleTest.cpp"> <ClCompile Include="LayerStyleTest.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="EPaperTest.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="EPaperTest.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>