mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 13:43:43 +00:00

This is necessary for acquiring current format size. @Issue: https://github.com/thorvg/thorvg/issues/976
389 lines
11 KiB
C++
389 lines
11 KiB
C++
/*
|
|
* Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
#include "tvgGlRenderer.h"
|
|
#include "tvgGlGpuBuffer.h"
|
|
#include "tvgGlGeometry.h"
|
|
#include "tvgGlPropertyInterface.h"
|
|
|
|
/************************************************************************/
|
|
/* Internal Class Implementation */
|
|
/************************************************************************/
|
|
static int32_t initEngineCnt = false;
|
|
static int32_t rendererCnt = 0;
|
|
|
|
|
|
static void _termEngine()
|
|
{
|
|
if (rendererCnt > 0) return;
|
|
|
|
//TODO: Clean up global resources
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* External Class Implementation */
|
|
/************************************************************************/
|
|
|
|
#define NOISE_LEVEL 0.5f
|
|
|
|
bool GlRenderer::clear()
|
|
{
|
|
//TODO: (Request) to clear target
|
|
// Will be adding glClearColor for input buffer
|
|
return true;
|
|
}
|
|
|
|
|
|
bool GlRenderer::target(TVG_UNUSED uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h)
|
|
{
|
|
assert(w > 0 && h > 0);
|
|
|
|
surface.stride = stride;
|
|
surface.w = w;
|
|
surface.h = h;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool GlRenderer::sync()
|
|
{
|
|
GL_CHECK(glFinish());
|
|
GlRenderTask::unload();
|
|
return true;
|
|
}
|
|
|
|
|
|
RenderRegion GlRenderer::region(TVG_UNUSED RenderData data)
|
|
{
|
|
return {0, 0, 0, 0};
|
|
}
|
|
|
|
|
|
bool GlRenderer::preRender()
|
|
{
|
|
if (mRenderTasks.size() == 0)
|
|
{
|
|
initShaders();
|
|
}
|
|
GlRenderTask::unload();
|
|
|
|
// Blend function for straight alpha
|
|
GL_CHECK(glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
|
|
GL_CHECK(glEnable(GL_BLEND));
|
|
return true;
|
|
}
|
|
|
|
|
|
bool GlRenderer::postRender()
|
|
{
|
|
//TODO: called just after render()
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
Compositor* GlRenderer::target(TVG_UNUSED const RenderRegion& region)
|
|
{
|
|
//TODO: Prepare frameBuffer & Setup render target for composition
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
bool GlRenderer::beginComposite(TVG_UNUSED Compositor* cmp, CompositeMethod method, uint32_t opacity)
|
|
{
|
|
//TODO: delete the given compositor and restore the context
|
|
return false;
|
|
}
|
|
|
|
|
|
bool GlRenderer::endComposite(TVG_UNUSED Compositor* cmp)
|
|
{
|
|
//TODO: delete the given compositor and restore the context
|
|
return false;
|
|
}
|
|
|
|
|
|
ColorSpace GlRenderer::SwRenderer::colorSpace()
|
|
{
|
|
return surface->cs;
|
|
}
|
|
|
|
|
|
bool GlRenderer::renderImage(TVG_UNUSED void* data)
|
|
{
|
|
//TODO: render requested images
|
|
return false;
|
|
}
|
|
|
|
|
|
bool GlRenderer::renderShape(RenderData data)
|
|
{
|
|
auto sdata = static_cast<GlShape*>(data);
|
|
if (!sdata) return false;
|
|
|
|
uint8_t r = 0, g = 0, b = 0, a = 0;
|
|
size_t flags = static_cast<size_t>(sdata->updateFlag);
|
|
|
|
GL_CHECK(glViewport(0, 0, (GLsizei)sdata->viewWd, (GLsizei)sdata->viewHt));
|
|
|
|
uint32_t primitiveCount = sdata->geometry->getPrimitiveCount();
|
|
for (uint32_t i = 0; i < primitiveCount; ++i)
|
|
{
|
|
if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform))
|
|
{
|
|
auto gradient = sdata->rshape->fill;
|
|
if (gradient) drawPrimitive(*sdata, gradient, i, RenderUpdateFlag::Gradient);
|
|
}
|
|
|
|
if(flags & (RenderUpdateFlag::Color | RenderUpdateFlag::Transform))
|
|
{
|
|
sdata->rshape->fillColor(&r, &g, &b, &a);
|
|
if (a > 0)
|
|
{
|
|
drawPrimitive(*sdata, r, g, b, a, i, RenderUpdateFlag::Color);
|
|
}
|
|
}
|
|
|
|
if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform))
|
|
{
|
|
sdata->rshape->strokeColor(&r, &g, &b, &a);
|
|
if (a > 0)
|
|
{
|
|
drawPrimitive(*sdata, r, g, b, a, i, RenderUpdateFlag::Stroke);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool GlRenderer::dispose(RenderData data)
|
|
{
|
|
auto sdata = static_cast<GlShape*>(data);
|
|
if (!sdata) return false;
|
|
|
|
delete sdata;
|
|
return true;
|
|
}
|
|
|
|
|
|
RenderData GlRenderer::prepare(TVG_UNUSED Surface* surface, TVG_UNUSED const RenderMesh* mesh, TVG_UNUSED RenderData data, TVG_UNUSED const RenderTransform* transform, TVG_UNUSED uint32_t opacity, TVG_UNUSED Array<RenderData>& clips, TVG_UNUSED RenderUpdateFlag flags)
|
|
{
|
|
//TODO:
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
RenderData GlRenderer::prepare(TVG_UNUSED const Array<RenderData>& scene, TVG_UNUSED RenderData data, TVG_UNUSED const RenderTransform* transform, TVG_UNUSED uint32_t opacity, TVG_UNUSED Array<RenderData>& clips, TVG_UNUSED RenderUpdateFlag flags)
|
|
{
|
|
//TODO:
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
RenderData GlRenderer::prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, TVG_UNUSED uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags, TVG_UNUSED bool clipper)
|
|
{
|
|
//prepare shape data
|
|
GlShape* sdata = static_cast<GlShape*>(data);
|
|
if (!sdata) {
|
|
sdata = new GlShape;
|
|
sdata->rshape = &rshape;
|
|
}
|
|
|
|
sdata->viewWd = static_cast<float>(surface.w);
|
|
sdata->viewHt = static_cast<float>(surface.h);
|
|
sdata->updateFlag = flags;
|
|
|
|
if (sdata->updateFlag == RenderUpdateFlag::None) return sdata;
|
|
|
|
sdata->geometry = make_unique<GlGeometry>();
|
|
|
|
//invisible?
|
|
uint8_t alphaF = 0, alphaS = 0;
|
|
rshape.fillColor(nullptr, nullptr, nullptr, &alphaF);
|
|
rshape.strokeColor(nullptr, nullptr, nullptr, &alphaS);
|
|
auto strokeWd = rshape.strokeWidth();
|
|
|
|
if ( ((sdata->updateFlag & RenderUpdateFlag::Gradient) == 0) &&
|
|
((sdata->updateFlag & RenderUpdateFlag::Color) && alphaF == 0) &&
|
|
((sdata->updateFlag & RenderUpdateFlag::Stroke) && alphaS == 0) )
|
|
{
|
|
return sdata;
|
|
}
|
|
|
|
sdata->geometry->updateTransform(transform, sdata->viewWd, sdata->viewHt);
|
|
|
|
if (sdata->updateFlag & (RenderUpdateFlag::Color | RenderUpdateFlag::Stroke | RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform) )
|
|
{
|
|
if (!sdata->geometry->decomposeOutline(rshape)) return sdata;
|
|
if (!sdata->geometry->generateAAPoints(rshape, static_cast<float>(strokeWd), sdata->updateFlag)) return sdata;
|
|
if (!sdata->geometry->tesselate(rshape, sdata->viewWd, sdata->viewHt, sdata->updateFlag)) return sdata;
|
|
}
|
|
return sdata;
|
|
}
|
|
|
|
|
|
RenderRegion GlRenderer::viewport()
|
|
{
|
|
return {0, 0, INT32_MAX, INT32_MAX};
|
|
}
|
|
|
|
|
|
bool GlRenderer::viewport(TVG_UNUSED const RenderRegion& vp)
|
|
{
|
|
//TODO:
|
|
return true;
|
|
}
|
|
|
|
|
|
int GlRenderer::init(uint32_t threads)
|
|
{
|
|
if ((initEngineCnt++) > 0) return true;
|
|
|
|
//TODO:
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
int32_t GlRenderer::init()
|
|
{
|
|
return initEngineCnt;
|
|
}
|
|
|
|
|
|
int GlRenderer::term()
|
|
{
|
|
if ((--initEngineCnt) > 0) return true;
|
|
|
|
initEngineCnt = 0;
|
|
|
|
_termEngine();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
GlRenderer* GlRenderer::gen()
|
|
{
|
|
return new GlRenderer();
|
|
}
|
|
|
|
|
|
GlRenderer::~GlRenderer()
|
|
{
|
|
mRenderTasks.clear();
|
|
|
|
--rendererCnt;
|
|
|
|
if (rendererCnt == 0 && initEngineCnt == 0) _termEngine();
|
|
}
|
|
|
|
|
|
void GlRenderer::initShaders()
|
|
{
|
|
// Solid Color Renderer
|
|
mRenderTasks.push_back(GlColorRenderTask::gen());
|
|
|
|
// Linear Gradient Renderer
|
|
mRenderTasks.push_back(GlLinearGradientRenderTask::gen());
|
|
|
|
// Radial Gradient Renderer
|
|
mRenderTasks.push_back(GlRadialGradientRenderTask::gen());
|
|
}
|
|
|
|
|
|
void GlRenderer::drawPrimitive(GlShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a, uint32_t primitiveIndex, RenderUpdateFlag flag)
|
|
{
|
|
GlColorRenderTask* renderTask = static_cast<GlColorRenderTask*>(mRenderTasks[GlRenderTask::RenderTypes::RT_Color].get());
|
|
assert(renderTask);
|
|
renderTask->load();
|
|
float* matrix = sdata.geometry->getTransforMatrix();
|
|
PropertyInterface::clearData(renderTask);
|
|
renderTask->setColor(r, g, b, a);
|
|
renderTask->setTransform(FORMAT_SIZE_MAT_4x4, matrix);
|
|
int32_t vertexLoc = renderTask->getLocationPropertyId();
|
|
renderTask->uploadValues();
|
|
sdata.geometry->draw(vertexLoc, primitiveIndex, flag);
|
|
sdata.geometry->disableVertex(vertexLoc);
|
|
|
|
}
|
|
|
|
|
|
void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, uint32_t primitiveIndex, RenderUpdateFlag flag)
|
|
{
|
|
const Fill::ColorStop* stops = nullptr;
|
|
auto stopCnt = fill->colorStops(&stops);
|
|
if (stopCnt < 2) return;
|
|
|
|
GlGradientRenderTask* rTask = nullptr;
|
|
auto size = sdata.geometry->getPrimitiveSize(primitiveIndex);
|
|
auto matrix = sdata.geometry->getTransforMatrix();
|
|
|
|
switch (fill->identifier()) {
|
|
case TVG_CLASS_ID_LINEAR: {
|
|
float x1, y1, x2, y2;
|
|
GlLinearGradientRenderTask *renderTask = static_cast<GlLinearGradientRenderTask*>(mRenderTasks[GlRenderTask::RenderTypes::RT_LinGradient].get());
|
|
assert(renderTask);
|
|
rTask = renderTask;
|
|
renderTask->load();
|
|
PropertyInterface::clearData(renderTask);
|
|
const LinearGradient* grad = static_cast<const LinearGradient*>(fill);
|
|
grad->linear(&x1, &y1, &x2, &y2);
|
|
renderTask->setStartPosition(x1, y1);
|
|
renderTask->setEndPosition(x2, y2);
|
|
break;
|
|
}
|
|
case TVG_CLASS_ID_RADIAL: {
|
|
float x1, y1, r1;
|
|
GlRadialGradientRenderTask *renderTask = static_cast<GlRadialGradientRenderTask*>(mRenderTasks[GlRenderTask::RenderTypes::RT_RadGradient].get());
|
|
assert(renderTask);
|
|
rTask = renderTask;
|
|
renderTask->load();
|
|
PropertyInterface::clearData(renderTask);
|
|
const RadialGradient* grad = static_cast<const RadialGradient*>(fill);
|
|
grad->radial(&x1, &y1, &r1);
|
|
renderTask->setStartPosition(x1, y1);
|
|
renderTask->setStartRadius(r1);
|
|
break;
|
|
}
|
|
}
|
|
if (rTask) {
|
|
auto vertexLoc = rTask->getLocationPropertyId();
|
|
rTask->setPrimitveSize(size.x, size.y);
|
|
rTask->setCanvasSize(sdata.viewWd, sdata.viewHt);
|
|
rTask->setNoise(NOISE_LEVEL);
|
|
rTask->setStopCount((int)stopCnt);
|
|
rTask->setTransform(FORMAT_SIZE_MAT_4x4, matrix);
|
|
|
|
for (uint32_t i = 0; i < stopCnt; ++i) {
|
|
rTask->setStopColor(i, stops[i].offset, stops[i].r, stops[i].g, stops[i].b, stops[i].a);
|
|
}
|
|
|
|
rTask->uploadValues();
|
|
sdata.geometry->draw(vertexLoc, primitiveIndex, flag);
|
|
sdata.geometry->disableVertex(vertexLoc);
|
|
}
|
|
}
|
|
|