初步实现图元绘制到QImage的接口
parent
9e1ec3f848
commit
994c711e34
|
@ -133,6 +133,7 @@
|
|||
<ClCompile Include="src\Renderer\Painting\Painting.cpp" />
|
||||
<ClCompile Include="src\Renderer\Painting\PaintingHelper.cpp" />
|
||||
<ClCompile Include="src\Renderer\PaintingMesh.cpp" />
|
||||
<ClCompile Include="src\Renderer\Preview\ElementRenderer.cpp" />
|
||||
<ClCompile Include="src\Renderer\RendererGLWidget.cpp" />
|
||||
<ClCompile Include="src\Renderer\RendererWidget.cpp" />
|
||||
<ClCompile Include="src\Renderer\Painting\ShortCutTree.cpp" />
|
||||
|
@ -152,6 +153,7 @@
|
|||
<None Include="lightstyle.qss" />
|
||||
<None Include="Shaders\depth_init.comp" />
|
||||
<None Include="Shaders\depth_mipmap.comp" />
|
||||
<None Include="Shaders\element.comp" />
|
||||
<None Include="Shaders\final.frag" />
|
||||
<None Include="Shaders\final.vert" />
|
||||
<None Include="Shaders\model.frag" />
|
||||
|
@ -159,7 +161,9 @@
|
|||
<None Include="Shaders\model_shadow.frag" />
|
||||
<None Include="Shaders\model_shadow.geom" />
|
||||
<None Include="Shaders\model_shadow.vert" />
|
||||
<None Include="Shaders\painting.comp" />
|
||||
<None Include="Shaders\painting.comp">
|
||||
<FileType>Document</FileType>
|
||||
</None>
|
||||
<None Include="Shaders\painting.frag" />
|
||||
<None Include="Shaders\painting.vert" />
|
||||
<None Include="Shaders\shader.frag" />
|
||||
|
@ -188,6 +192,7 @@
|
|||
<ClInclude Include="src\Renderer\Painting\LineTree.h" />
|
||||
<ClInclude Include="src\Renderer\Painting\MaterialStyleStroke.h" />
|
||||
<ClInclude Include="src\Renderer\Painting\Painting.h" />
|
||||
<ClInclude Include="src\Renderer\Preview\ElementRenderer.h" />
|
||||
<ClInclude Include="src\SvgParser.h" />
|
||||
<QtMoc Include="src\TitleWidget.h" />
|
||||
<QtMoc Include="src\IconWidget.h" />
|
||||
|
|
|
@ -59,6 +59,12 @@
|
|||
<Filter Include="Source Files\Editor\util">
|
||||
<UniqueIdentifier>{96f98afe-4250-44cb-a505-682a1d5932c3}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Renderer\Preview">
|
||||
<UniqueIdentifier>{2a8e109f-7791-46ad-8c86-fe22a651cbe7}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\Renderer\Preview">
|
||||
<UniqueIdentifier>{7ead1a66-586a-4584-ae80-9e7a4e667364}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<QtUic Include="MainWindow.ui">
|
||||
|
@ -192,6 +198,9 @@
|
|||
<ClCompile Include="src\Editor\util\SvgFileLoader.cpp">
|
||||
<Filter>Source Files\Editor\util</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\Renderer\Preview\ElementRenderer.cpp">
|
||||
<Filter>Source Files\Renderer\Preview</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<QtMoc Include="src\Renderer\RendererGLWidget.h">
|
||||
|
@ -284,6 +293,9 @@
|
|||
<Filter>Resource Files</Filter>
|
||||
</None>
|
||||
<None Include="..\data.json" />
|
||||
<None Include="Shaders\element.comp">
|
||||
<Filter>Resource Files\Shaders</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<QtUic Include="EditorWidget.ui">
|
||||
|
@ -360,9 +372,6 @@
|
|||
<ClInclude Include="src\Renderer\Painting\CubicBezierSignedDistance.h">
|
||||
<Filter>Header Files\Renderer\Painting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\Editor\third-party modules\util\SvgFileLoader.h">
|
||||
<Filter>Header Files\Editor\util</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\Renderer\Painting\ElementStyle.h">
|
||||
<Filter>Header Files\Renderer\Painting</Filter>
|
||||
</ClInclude>
|
||||
|
@ -387,12 +396,12 @@
|
|||
<ClInclude Include="src\Renderer\Painting\MaterialStyleFill.h">
|
||||
<Filter>Header Files\Renderer\Painting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\Renderer\Painting\BaseStyle.h">
|
||||
<Filter>Header Files\Renderer\Painting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\Renderer\Painting\MaterialStyleStroke.h">
|
||||
<Filter>Header Files\Renderer\Painting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\Renderer\Preview\ElementRenderer.h">
|
||||
<Filter>Header Files\Renderer\Preview</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<QtRcc Include="MainWindow.qrc">
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
<file>images/icon_window_restore.png</file>
|
||||
<file>darkstyle.qss</file>
|
||||
<file>lightstyle.qss</file>
|
||||
<file>Shaders/element.comp</file>
|
||||
</qresource>
|
||||
<qresource prefix="/qt/etc">
|
||||
<file>qt.conf</file>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,5 @@
|
|||
#include "GraphicElement.h"
|
||||
#include "third-party modules/util/SvgFileLoader.h"
|
||||
#include "util/SvgFileLoader.h"
|
||||
using namespace std;
|
||||
QPainterPath SimpleElement::getPaintObject() const
|
||||
{
|
||||
|
|
|
@ -1 +1,16 @@
|
|||
#include "Element.h"
|
||||
|
||||
void Renderer::ElementTransform::applyTransformStyle(const TransformStyle& t)
|
||||
{
|
||||
center += t.translation;
|
||||
size *= t.scale;
|
||||
rotation += t.rotation;
|
||||
flip ^= t.flip;
|
||||
}
|
||||
|
||||
Renderer::ElementTransform Renderer::ElementTransform::appliedTransformStyle(const TransformStyle& t) const
|
||||
{
|
||||
ElementTransform result = *this;
|
||||
result.applyTransformStyle(t);
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -21,5 +21,8 @@ namespace Renderer
|
|||
float rotation; /// ½Ç¶ÈÖÆ
|
||||
glm::bvec2 flip;
|
||||
GLuint zIndex;
|
||||
|
||||
void applyTransformStyle(const TransformStyle& t);
|
||||
ElementTransform appliedTransformStyle(const TransformStyle& t) const;
|
||||
};
|
||||
}
|
|
@ -79,10 +79,7 @@ void Renderer::Painting::addElement(const Element& element, const ElementTransfo
|
|||
{
|
||||
auto& style = it->second[i];
|
||||
ElementTransform trans = transform;
|
||||
trans.center += style.transform->translation;
|
||||
trans.size *= style.transform->scale;
|
||||
trans.rotation += style.transform->rotation;
|
||||
trans.flip ^= style.transform->flip;
|
||||
trans.applyTransformStyle(*style.transform);
|
||||
trans.zIndex = trans.zIndex * 10 + i;
|
||||
addElement(ElementWithTransform{ BaseElement{element.contour, style.material}, BaseTransform(trans) });
|
||||
}
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
#include "ElementRenderer.h"
|
||||
#include <QOpenGLFramebufferObject>
|
||||
#include <QOpenGLPaintDevice>
|
||||
#include <QDebug>
|
||||
#include <QSurface>
|
||||
#include <QPainterPath>
|
||||
#include <QPainter>
|
||||
#include <limits>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include "../Painting/Element.h"
|
||||
#include "../Painting/Painting.h"
|
||||
#include "../Painting/MaterialStyleStroke.h"
|
||||
using namespace Renderer;
|
||||
|
||||
Renderer::ElementRenderer::ElementRenderer(QOpenGLWidget* glWidget)
|
||||
: glWidget(glWidget)
|
||||
{
|
||||
}
|
||||
|
||||
void Renderer::ElementRenderer::initialize()
|
||||
{
|
||||
initializeOpenGLFunctions();
|
||||
shader = std::make_shared<QOpenGLShaderProgram>();
|
||||
if (!shader->addShaderFromSourceFile(QOpenGLShader::Compute, ":/Shaders/element.comp"))
|
||||
qDebug() << "ERROR: " << shader->log();
|
||||
if (!shader->link())
|
||||
qDebug() << "ERROR: " << shader->log();
|
||||
|
||||
}
|
||||
|
||||
std::vector<glm::vec2> generatePathBuffer(const QPainterPath& path)
|
||||
{
|
||||
std::vector<glm::vec2> pathBuffer;
|
||||
for (int i = 0; i < path.elementCount(); i++)
|
||||
{
|
||||
QPainterPath::Element element = path.elementAt(i);
|
||||
switch (element.type)
|
||||
{
|
||||
case QPainterPath::MoveToElement:
|
||||
qDebug() << "MoveToElement";
|
||||
pathBuffer.push_back(glm::vec2(std::numeric_limits<float>::infinity()));
|
||||
pathBuffer.push_back(glm::vec2(element.x, element.y));
|
||||
break;
|
||||
case QPainterPath::LineToElement:
|
||||
qDebug() << "LineToElement";
|
||||
glm::vec2 end = glm::vec2(element.x, element.y);
|
||||
glm::vec2 mid = (pathBuffer.back() + end) / 2.f;
|
||||
pathBuffer.push_back(mid);
|
||||
pathBuffer.push_back(mid);
|
||||
pathBuffer.push_back(end);
|
||||
break;
|
||||
case QPainterPath::CurveToElement:
|
||||
qDebug() << "CurveToElement";
|
||||
pathBuffer.push_back(glm::vec2(element.x, element.y));
|
||||
break;
|
||||
case QPainterPath::CurveToDataElement:
|
||||
qDebug() << "CurveToDataElement";
|
||||
pathBuffer.push_back(glm::vec2(element.x, element.y));
|
||||
break;
|
||||
}
|
||||
qDebug() << element;
|
||||
}
|
||||
return pathBuffer;
|
||||
}
|
||||
|
||||
std::vector<GLfloat> generateStyleBuffer(const std::vector<BaseStyle>& styles)
|
||||
{
|
||||
std::vector<GLfloat> styleBuffer;
|
||||
for (auto& style : styles)
|
||||
{
|
||||
styleBuffer.push_back(style.transform->translation.x);
|
||||
styleBuffer.push_back(style.transform->translation.y);
|
||||
styleBuffer.push_back(style.transform->scale.x);
|
||||
styleBuffer.push_back(style.transform->scale.y);
|
||||
styleBuffer.push_back(style.transform->rotation);
|
||||
styleBuffer.push_back(glm::uintBitsToFloat(glm::packUnorm2x16(style.transform->flip)));
|
||||
auto encoded = style.material->encoded();
|
||||
styleBuffer.insert(styleBuffer.end(), encoded.begin(), encoded.end());
|
||||
}
|
||||
return styleBuffer;
|
||||
}
|
||||
|
||||
QRectF calcBoundingRect(const QPainterPath& path, const std::vector<BaseStyle>& styles)
|
||||
{
|
||||
QRectF bound = path.boundingRect();
|
||||
|
||||
ElementTransform originTransform{ glm::vec2(bound.center().x(), bound.center().y()), glm::vec2(bound.width(), bound.height()), 0, glm::bvec2(false), 0 };
|
||||
|
||||
glm::vec2 leftTop(std::numeric_limits<float>::max()), rightBottom(std::numeric_limits<float>::min());
|
||||
|
||||
for (auto& baseStyle : styles)
|
||||
{
|
||||
BaseTransform transform(originTransform.appliedTransformStyle(*baseStyle.transform));
|
||||
if (baseStyle.material->type() == MaterialStyleType::kStroke)
|
||||
{
|
||||
float halfWidth = std::static_pointer_cast<MaterialStyleStroke>(baseStyle.material)->getWidth() / 2;
|
||||
transform.bound.x -= halfWidth;
|
||||
transform.bound.y -= halfWidth;
|
||||
transform.bound.z += halfWidth;
|
||||
transform.bound.w += halfWidth;
|
||||
}
|
||||
glm::vec2 points[] = {
|
||||
glm::vec2(transform.bound.x, transform.bound.y),
|
||||
glm::vec2(transform.bound.x, transform.bound.w),
|
||||
glm::vec2(transform.bound.z, transform.bound.w),
|
||||
glm::vec2(transform.bound.z, transform.bound.y)
|
||||
};
|
||||
for (auto& point : points)
|
||||
{
|
||||
float angle = glm::radians(baseStyle.transform->rotation);
|
||||
point = glm::mat2{ {cos(angle), -sin(angle)}, {sin(angle), cos(angle)} } *point;
|
||||
leftTop = glm::min(leftTop, point);
|
||||
rightBottom = glm::max(rightBottom, point);
|
||||
}
|
||||
}
|
||||
return QRectF(QPointF(leftTop.x, leftTop.y), QPointF(rightBottom.x, rightBottom.y));
|
||||
}
|
||||
|
||||
std::pair<QImage, QPointF> Renderer::ElementRenderer::drawElement(const QPainterPath& path, const ElementStyle& style, float pixelRatio, bool releaseContext)
|
||||
{
|
||||
auto baseStyles = style.toBaseStyles();
|
||||
QRectF bound = calcBoundingRect(path, baseStyles);
|
||||
QPointF leftTop = bound.topLeft();
|
||||
QSize size(ceil(bound.width() * pixelRatio), ceil(bound.height() * pixelRatio));
|
||||
|
||||
auto pathBuffer = generatePathBuffer(path);
|
||||
auto styleBuffer = generateStyleBuffer(baseStyles);
|
||||
|
||||
if (releaseContext) glWidget->makeCurrent();
|
||||
|
||||
GLuint ssbo[2];
|
||||
glGenBuffers(2, ssbo);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo[0]);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, pathBuffer.size() * sizeof(glm::vec2), pathBuffer.data(), GL_STATIC_READ);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo[1]);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, styleBuffer.size() * sizeof(GLfloat), styleBuffer.data(), GL_STATIC_READ);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
|
||||
|
||||
auto fbo = QOpenGLFramebufferObject(size, QOpenGLFramebufferObject::NoAttachment, GL_TEXTURE_2D);
|
||||
fbo.bind();
|
||||
glClearColor(0, 0, 0, 0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
fbo.release();
|
||||
|
||||
shader->bind();
|
||||
shader->setUniformValue("pathSize", (GLint)pathBuffer.size());
|
||||
shader->setUniformValue("styleSize", (GLint)styleBuffer.size());
|
||||
shader->setUniformValue("leftTop", leftTop);
|
||||
shader->setUniformValue("pixelRatio", pixelRatio);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 10, ssbo[0]);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 11, ssbo[1]);
|
||||
glBindImageTexture(0, fbo.texture(), 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8);
|
||||
glDispatchCompute(ceil(size.width() / 8.), ceil(size.height() / 8.), 1);
|
||||
shader->release();
|
||||
|
||||
auto image = fbo.toImage(false);
|
||||
glDeleteBuffers(2, ssbo);
|
||||
if (releaseContext) glWidget->doneCurrent();
|
||||
return { image, leftTop };
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
#include <QOpenGLWidget>
|
||||
#include <QOpenGLFunctions_4_5_Core>
|
||||
#include <QOPenGLShaderProgram>
|
||||
#include "../Painting/ElementStyle.h"
|
||||
|
||||
namespace Renderer
|
||||
{
|
||||
class ElementRenderer : protected QOpenGLFunctions_4_5_Core
|
||||
{
|
||||
public:
|
||||
ElementRenderer(QOpenGLWidget* glWidget);
|
||||
|
||||
/**
|
||||
* @brief 须在initializeGL函数中调用
|
||||
*/
|
||||
void initialize();
|
||||
|
||||
/**
|
||||
* @brief 将图元绘制到QImage
|
||||
* @param path 图元几何数据
|
||||
* @param style 图元样式
|
||||
* @param pixelRatio path中的单位坐标对应的像素大小
|
||||
* @param releaseContext 若在initializeGL, resizeGL或paintGL中调用须传入false
|
||||
* @return QImage 绘制得到的位图
|
||||
* @return QPointF 位图原点(左上角)在QPainterPath坐标系下的坐标
|
||||
*/
|
||||
std::pair<QImage, QPointF> drawElement(const QPainterPath& path, const ElementStyle& style, float pixelRatio, bool releaseContext = true);
|
||||
protected:
|
||||
QOpenGLWidget* glWidget;
|
||||
std::shared_ptr<QOpenGLShaderProgram> shader;
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue