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:
Hermet Park 2020-12-07 15:45:44 +09:00 committed by GitHub
parent 20de2bfc15
commit 77e8a195b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 237 additions and 68 deletions

View file

@ -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));
}

View file

@ -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

View file

@ -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;

View file

@ -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) {

View file

@ -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);

View file

@ -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;
};
}

View file

@ -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;
}

View file

@ -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)