mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-10 14:41:50 +00:00
sw_engine renderer: support scene opacity composition
this is an additional enhancement of af8c278c5e
Now scene opacity composition is supported.
Also, this implementaion fixes an incorrect scene bounding box computation.
Plus, adding stroking feathering to shape bounding box size.
This commit is contained in:
parent
20de2bfc15
commit
77e8a195b4
8 changed files with 237 additions and 68 deletions
|
@ -8,61 +8,79 @@ void tvgDrawCmds(tvg::Canvas* canvas)
|
|||
{
|
||||
if (!canvas) return;
|
||||
|
||||
//Create a Scene
|
||||
auto scene = tvg::Scene::gen();
|
||||
scene->opacity(175); //Apply opacity to scene (0 - 255)
|
||||
scene->reserve(2);
|
||||
|
||||
//Prepare Circle
|
||||
auto shape1 = tvg::Shape::gen();
|
||||
shape1->appendCircle(400, 400, 250, 250);
|
||||
shape1->fill(255, 255, 0, 255);
|
||||
canvas->push(move(shape1));
|
||||
scene->push(move(shape1));
|
||||
|
||||
//Create a Scene
|
||||
auto scene = tvg::Scene::gen();
|
||||
scene->opacity(127); //Apply opacity to scene (0 - 255)
|
||||
scene->reserve(2);
|
||||
|
||||
//Star
|
||||
//Round rectangle
|
||||
auto shape2 = tvg::Shape::gen();
|
||||
|
||||
//Appends Paths
|
||||
shape2->moveTo(199, 34);
|
||||
shape2->lineTo(253, 143);
|
||||
shape2->lineTo(374, 160);
|
||||
shape2->lineTo(287, 244);
|
||||
shape2->lineTo(307, 365);
|
||||
shape2->lineTo(199, 309);
|
||||
shape2->lineTo(97, 365);
|
||||
shape2->lineTo(112, 245);
|
||||
shape2->lineTo(26, 161);
|
||||
shape2->lineTo(146, 143);
|
||||
shape2->close();
|
||||
shape2->fill(0, 0, 255, 255);
|
||||
shape2->appendRect(450, 100, 200, 200, 50, 50);
|
||||
shape2->fill(0, 255, 0, 255);
|
||||
shape2->stroke(10);
|
||||
shape2->stroke(255, 255, 255, 255);
|
||||
shape2->opacity(127);
|
||||
|
||||
scene->push(move(shape2));
|
||||
|
||||
//Circle
|
||||
|
||||
//Draw the Scene onto the Canvas
|
||||
canvas->push(move(scene));
|
||||
|
||||
//Create a Scene 2
|
||||
auto scene2 = tvg::Scene::gen();
|
||||
scene2->opacity(127); //Apply opacity to scene (0 - 255)
|
||||
scene2->scale(1.2);
|
||||
scene2->reserve(2);
|
||||
|
||||
//Star
|
||||
auto shape3 = tvg::Shape::gen();
|
||||
|
||||
//Appends Paths
|
||||
shape3->moveTo(199, 34);
|
||||
shape3->lineTo(253, 143);
|
||||
shape3->lineTo(374, 160);
|
||||
shape3->lineTo(287, 244);
|
||||
shape3->lineTo(307, 365);
|
||||
shape3->lineTo(199, 309);
|
||||
shape3->lineTo(97, 365);
|
||||
shape3->lineTo(112, 245);
|
||||
shape3->lineTo(26, 161);
|
||||
shape3->lineTo(146, 143);
|
||||
shape3->close();
|
||||
shape3->fill(0, 0, 255, 255);
|
||||
shape3->stroke(10);
|
||||
shape3->stroke(255, 255, 255, 255);
|
||||
shape3->opacity(127);
|
||||
|
||||
scene2->push(move(shape3));
|
||||
|
||||
//Circle
|
||||
auto shape4 = tvg::Shape::gen();
|
||||
|
||||
auto cx = 550.0f;
|
||||
auto cy = 550.0f;
|
||||
auto radius = 125.0f;
|
||||
auto halfRadius = radius * 0.552284f;
|
||||
|
||||
//Append Paths
|
||||
shape3->moveTo(cx, cy - radius);
|
||||
shape3->cubicTo(cx + halfRadius, cy - radius, cx + radius, cy - halfRadius, cx + radius, cy);
|
||||
shape3->cubicTo(cx + radius, cy + halfRadius, cx + halfRadius, cy + radius, cx, cy+ radius);
|
||||
shape3->cubicTo(cx - halfRadius, cy + radius, cx - radius, cy + halfRadius, cx - radius, cy);
|
||||
shape3->cubicTo(cx - radius, cy - halfRadius, cx - halfRadius, cy - radius, cx, cy - radius);
|
||||
shape3->fill(255, 0, 0, 255);
|
||||
shape3->stroke(10);
|
||||
shape3->stroke(0, 0, 255, 255);
|
||||
shape3->opacity(200);
|
||||
scene->push(move(shape3));
|
||||
shape4->moveTo(cx, cy - radius);
|
||||
shape4->cubicTo(cx + halfRadius, cy - radius, cx + radius, cy - halfRadius, cx + radius, cy);
|
||||
shape4->cubicTo(cx + radius, cy + halfRadius, cx + halfRadius, cy + radius, cx, cy+ radius);
|
||||
shape4->cubicTo(cx - halfRadius, cy + radius, cx - radius, cy + halfRadius, cx - radius, cy);
|
||||
shape4->cubicTo(cx - radius, cy - halfRadius, cx - halfRadius, cy - radius, cx, cy - radius);
|
||||
shape4->fill(255, 0, 0, 255);
|
||||
shape4->stroke(10);
|
||||
shape4->stroke(0, 0, 255, 255);
|
||||
shape4->opacity(200);
|
||||
scene2->push(move(shape4));
|
||||
|
||||
//Draw the Scene onto the Canvas
|
||||
canvas->push(move(scene));
|
||||
canvas->push(move(scene2));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -96,6 +96,28 @@ bool GlRenderer::postRender()
|
|||
}
|
||||
|
||||
|
||||
void* GlRenderer::beginComposite(uint32_t x, uint32_t y, uint32_t w, uint32_t h)
|
||||
{
|
||||
//TODO: Prepare frameBuffer & Setup render target for composition
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
bool GlRenderer::endComposite(void* ctx, uint32_t opacity)
|
||||
{
|
||||
//TODO: Composite Framebuffer to main surface
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool GlRenderer::render(const Picture& picture, void *data)
|
||||
{
|
||||
//TODO Draw Bitmap Image
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool GlRenderer::render(const Shape& shape, void* data)
|
||||
{
|
||||
GlShape* sdata = static_cast<GlShape*>(data);
|
||||
|
@ -140,6 +162,13 @@ bool GlRenderer::dispose(void *data)
|
|||
}
|
||||
|
||||
|
||||
void* GlRenderer::prepare(TVG_UNUSED const Picture& picture, TVG_UNUSED void* data, TVG_UNUSED uint32_t *buffer, TVG_UNUSED const RenderTransform* transform, TVG_UNUSED uint32_t opacity, TVG_UNUSED vector<Composite>& compList, TVG_UNUSED RenderUpdateFlag flags)
|
||||
{
|
||||
//TODO:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void* GlRenderer::prepare(const Shape& shape, void* data, TVG_UNUSED const RenderTransform* transform, TVG_UNUSED uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flags)
|
||||
{
|
||||
//prepare shape data
|
||||
|
|
|
@ -31,9 +31,13 @@ public:
|
|||
Surface surface = {nullptr, 0, 0, 0};
|
||||
|
||||
void* prepare(const Shape& shape, void* data, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flags) override;
|
||||
void* prepare(const Picture& picture, void* data, uint32_t *buffer, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flags) override;
|
||||
bool dispose(void *data) override;
|
||||
void* beginComposite(uint32_t x, uint32_t y, uint32_t w, uint32_t h) override;
|
||||
bool endComposite(void* ctx, uint32_t opacity) override;
|
||||
bool preRender() override;
|
||||
bool render(const Shape& shape, void *data) override;
|
||||
bool render(const Picture& picture, void *data) override;
|
||||
bool postRender() override;
|
||||
bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h);
|
||||
bool sync() override;
|
||||
|
|
|
@ -30,6 +30,14 @@
|
|||
static bool initEngine = false;
|
||||
static uint32_t rendererCnt = 0;
|
||||
|
||||
struct CompositeCtx
|
||||
{
|
||||
SwSurface surface;
|
||||
SwSurface* recover;
|
||||
SwImage image;
|
||||
};
|
||||
|
||||
|
||||
struct SwTask : Task
|
||||
{
|
||||
Matrix* transform = nullptr;
|
||||
|
@ -219,6 +227,12 @@ bool SwRenderer::clear()
|
|||
}
|
||||
|
||||
|
||||
bool SwRenderer::sync()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t cs)
|
||||
{
|
||||
if (!buffer || stride == 0 || w == 0 || h == 0) return false;
|
||||
|
@ -268,6 +282,80 @@ bool SwRenderer::render(TVG_UNUSED const Picture& picture, void *data)
|
|||
}
|
||||
|
||||
|
||||
void* SwRenderer::beginComposite(uint32_t x, uint32_t y, uint32_t w, uint32_t h)
|
||||
{
|
||||
auto ctx = new CompositeCtx;
|
||||
if (!ctx) return nullptr;
|
||||
|
||||
//SwImage, Optimize Me: Surface size from MainSurface(WxH) to Parameter W x H
|
||||
ctx->image.data = (uint32_t*) malloc(sizeof(uint32_t) * mainSurface->w * mainSurface->h);
|
||||
if (!ctx->image.data) {
|
||||
delete(ctx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//Boundary Check
|
||||
if (x < 0) x = 0;
|
||||
if (y < 0) y = 0;
|
||||
if (x + w > mainSurface->w) w = (mainSurface->w - x);
|
||||
if (y + h > mainSurface->h) h = (mainSurface->h - y);
|
||||
|
||||
//FIXME: Should be removed if xywh is proper.
|
||||
x = 0;
|
||||
y = 0;
|
||||
w = mainSurface->w;
|
||||
h = mainSurface->h;
|
||||
|
||||
ctx->image.bbox.min.x = x;
|
||||
ctx->image.bbox.min.y = y;
|
||||
ctx->image.bbox.max.x = x + w;
|
||||
ctx->image.bbox.max.y = y + h;
|
||||
ctx->image.w = mainSurface->w;
|
||||
ctx->image.h = mainSurface->h;
|
||||
|
||||
//Inherits attributes from main surface
|
||||
ctx->surface.comp = mainSurface->comp;
|
||||
ctx->surface.stride = mainSurface->w;
|
||||
ctx->surface.cs = mainSurface->cs;
|
||||
|
||||
//We know partial clear region
|
||||
ctx->surface.buffer = ctx->image.data + (ctx->surface.stride * y + x);
|
||||
ctx->surface.w = w;
|
||||
ctx->surface.h = h;
|
||||
|
||||
rasterClear(&ctx->surface);
|
||||
|
||||
//Recover context
|
||||
ctx->surface.buffer = ctx->image.data;
|
||||
ctx->surface.w = ctx->image.w;
|
||||
ctx->surface.h = ctx->image.h;
|
||||
|
||||
//Switch render target
|
||||
ctx->recover = mainSurface;
|
||||
mainSurface = &ctx->surface;
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::endComposite(void* p, uint32_t opacity)
|
||||
{
|
||||
if (!p) return false;
|
||||
auto ctx = static_cast<CompositeCtx*>(p);
|
||||
|
||||
//Recover render target
|
||||
mainSurface = ctx->recover;
|
||||
|
||||
auto ret = rasterImage(mainSurface, &ctx->image, nullptr, opacity);
|
||||
|
||||
//Free resources
|
||||
free(ctx->image.data);
|
||||
delete(ctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::prepareComposite(const SwShapeTask* task, SwImage* image)
|
||||
{
|
||||
if (!compSurface) {
|
||||
|
|
|
@ -38,13 +38,16 @@ class SwRenderer : public RenderMethod
|
|||
public:
|
||||
void* prepare(const Shape& shape, void* data, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flags) override;
|
||||
void* prepare(const Picture& picture, void* data, uint32_t *buffer, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flags) override;
|
||||
void* beginComposite(uint32_t x, uint32_t y, uint32_t w, uint32_t h) override;
|
||||
bool endComposite(void* ctx, uint32_t opacity) override;
|
||||
bool dispose(void *data) override;
|
||||
bool preRender() override;
|
||||
bool postRender() override;
|
||||
bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t cs);
|
||||
bool clear() override;
|
||||
bool render(const Shape& shape, void *data) override;
|
||||
bool render(const Picture& picture, void *data) override;
|
||||
bool sync() override;
|
||||
bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t cs);
|
||||
|
||||
static SwRenderer* gen();
|
||||
static bool init(uint32_t threads);
|
||||
|
|
|
@ -65,15 +65,17 @@ class RenderMethod
|
|||
{
|
||||
public:
|
||||
virtual ~RenderMethod() {}
|
||||
virtual void* prepare(TVG_UNUSED const Shape& shape, TVG_UNUSED void* data, TVG_UNUSED const RenderTransform* transform, uint32_t opacity, TVG_UNUSED vector<Composite>& compList, TVG_UNUSED RenderUpdateFlag flags) { return nullptr; }
|
||||
virtual void* prepare(TVG_UNUSED const Picture& picture, TVG_UNUSED void* data, TVG_UNUSED uint32_t *buffer, TVG_UNUSED const RenderTransform* transform, TVG_UNUSED uint32_t opacity, TVG_UNUSED vector<Composite>& compList, TVG_UNUSED RenderUpdateFlag flags) { return nullptr; }
|
||||
virtual bool dispose(TVG_UNUSED void *data) { return true; }
|
||||
virtual bool preRender() { return true; }
|
||||
virtual bool render(TVG_UNUSED const Shape& shape, TVG_UNUSED void *data) { return true; }
|
||||
virtual bool render(TVG_UNUSED const Picture& picture, TVG_UNUSED void *data) { return true; }
|
||||
virtual bool postRender() { return true; }
|
||||
virtual bool clear() { return true; }
|
||||
virtual bool sync() { return true; }
|
||||
virtual void* prepare(const Shape& shape, void* data, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flags) = 0;
|
||||
virtual void* prepare(const Picture& picture, void* data, uint32_t *buffer, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flags) = 0;
|
||||
virtual void* beginComposite(uint32_t x, uint32_t y, uint32_t w, uint32_t h) = 0;
|
||||
virtual bool endComposite(void* ctx, uint32_t opacity) = 0;
|
||||
virtual bool dispose(void *data) = 0;
|
||||
virtual bool preRender() = 0;
|
||||
virtual bool render(const Shape& shape, void *data) = 0;
|
||||
virtual bool render(const Picture& picture, void *data) = 0;
|
||||
virtual bool postRender() = 0;
|
||||
virtual bool clear() = 0;
|
||||
virtual bool sync() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
struct Scene::Impl
|
||||
{
|
||||
vector<Paint*> paints;
|
||||
uint32_t opacity;
|
||||
|
||||
bool dispose(RenderMethod& renderer)
|
||||
{
|
||||
|
@ -46,6 +47,12 @@ struct Scene::Impl
|
|||
|
||||
void* update(RenderMethod &renderer, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flag)
|
||||
{
|
||||
this->opacity = opacity;
|
||||
|
||||
/* Overriding opacity value. If this scene is half-translucent,
|
||||
It must do intermeidate composition with that opacity value. */
|
||||
if (opacity < 255 && opacity > 0) opacity = 255;
|
||||
|
||||
/* FXIME: it requires to return list of children engine data
|
||||
This is necessary for scene composition */
|
||||
void* edata = nullptr;
|
||||
|
@ -59,45 +66,54 @@ struct Scene::Impl
|
|||
|
||||
bool render(RenderMethod &renderer)
|
||||
{
|
||||
//TODO: composition begin
|
||||
//auto data = renderer.beginComp();
|
||||
void* ctx = nullptr;
|
||||
|
||||
//Half translucent. This requires intermediate composition.
|
||||
if (opacity < 255 && opacity > 0) {
|
||||
//FIXME: Get Render Boundary of Shapes.
|
||||
//float x, y, w, h;
|
||||
//if (!bounds(&x, &y, &w, &h)) return false;
|
||||
//ctx = renderer.beginComposite(roundf(x), roundf(y), roundf(w), roundf(h));
|
||||
ctx = renderer.beginComposite(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
for (auto paint : paints) {
|
||||
if (!paint->pImpl->render(renderer)) return false;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: composition end
|
||||
//renderer.endComp(edata);
|
||||
if (ctx) return renderer.endComposite(ctx, opacity);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bounds(float* px, float* py, float* pw, float* ph)
|
||||
{
|
||||
auto x = FLT_MAX;
|
||||
auto y = FLT_MAX;
|
||||
auto w = 0.0f;
|
||||
auto h = 0.0f;
|
||||
if (paints.size() == 0) return false;
|
||||
|
||||
auto x1 = FLT_MAX;
|
||||
auto y1 = FLT_MAX;
|
||||
auto x2 = 0.0f;
|
||||
auto y2 = 0.0f;
|
||||
|
||||
for (auto paint : paints) {
|
||||
auto x2 = FLT_MAX;
|
||||
auto y2 = FLT_MAX;
|
||||
auto w2 = 0.0f;
|
||||
auto h2 = 0.0f;
|
||||
auto x = FLT_MAX;
|
||||
auto y = FLT_MAX;
|
||||
auto w = 0.0f;
|
||||
auto h = 0.0f;
|
||||
|
||||
if (!paint->pImpl->bounds(&x2, &y2, &w2, &h2)) continue;
|
||||
if (!paint->pImpl->bounds(&x, &y, &w, &h)) continue;
|
||||
|
||||
//Merge regions
|
||||
if (x2 < x) x = x2;
|
||||
if (x + w < x2 + w2) w = (x2 + w2) - x;
|
||||
if (y2 < y) y = y2;
|
||||
if (y + h < y2 + h2) h = (y2 + h2) - y;
|
||||
if (x < x1) x1 = x;
|
||||
if (x2 < x + w) x2 = (x + w);
|
||||
if (y < y1) y1 = y;
|
||||
if (y2 < y + h) y2 = (y + h);
|
||||
}
|
||||
|
||||
if (px) *px = x;
|
||||
if (py) *py = y;
|
||||
if (pw) *pw = w;
|
||||
if (ph) *ph = h;
|
||||
if (px) *px = x1;
|
||||
if (py) *py = y1;
|
||||
if (pw) *pw = (x2 - x1);
|
||||
if (ph) *ph = (y2 - y1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -232,7 +232,16 @@ struct Shape::Impl
|
|||
|
||||
bool bounds(float* x, float* y, float* w, float* h)
|
||||
{
|
||||
return path.bounds(x, y, w, h);
|
||||
auto ret = path.bounds(x, y, w, h);
|
||||
|
||||
//Stroke feathering
|
||||
if (stroke) {
|
||||
if (x) *x -= stroke->width * 0.5f;
|
||||
if (y) *y -= stroke->width * 0.5f;
|
||||
if (w) *w += stroke->width;
|
||||
if (h) *h += stroke->width;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool strokeWidth(float width)
|
||||
|
|
Loading…
Add table
Reference in a new issue