mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 13:43:43 +00:00
common paint: introduce opacity() method.
We introduced separate opacity interface to adjust alpha value by paint. This opacity will affect to whole paint image if paint is a group of paints. Also, this opacity is to multipy with fill/stroke alpha values. This means if the opacity is valid, the paint might deal with a composition step, which is very expensive due to additional rendering step. One tip is, if you want to toggle on/off for a certian paint, you can set opacity to 255 or 0. @API Additions: Result Paint::opacity(uint8_t o) noexcept; uint8_t Paint::opacity() const noexcept; @Examples: examples/Opacity @Issues: 94
This commit is contained in:
parent
da4b57e763
commit
0399d84478
14 changed files with 223 additions and 21 deletions
|
@ -93,10 +93,13 @@ public:
|
|||
Result translate(float x, float y) noexcept;
|
||||
Result transform(const Matrix& m) noexcept;
|
||||
Result bounds(float* x, float* y, float* w, float* h) const noexcept;
|
||||
Result opacity(uint8_t o) noexcept;
|
||||
Paint* duplicate() const noexcept;
|
||||
|
||||
Result composite(std::unique_ptr<Paint> target, CompositeMethod method) const noexcept;
|
||||
|
||||
uint8_t opacity() const noexcept;
|
||||
|
||||
_TVG_DECLARE_ACCESSOR();
|
||||
_TVG_DECLARE_PRIVATE(Paint);
|
||||
};
|
||||
|
|
166
src/examples/Opacity.cpp
Normal file
166
src/examples/Opacity.cpp
Normal file
|
@ -0,0 +1,166 @@
|
|||
#include "Common.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Drawing Commands */
|
||||
/************************************************************************/
|
||||
|
||||
void tvgDrawCmds(tvg::Canvas* canvas)
|
||||
{
|
||||
if (!canvas) return;
|
||||
|
||||
//Prepare Circle
|
||||
auto shape1 = tvg::Shape::gen();
|
||||
shape1->appendCircle(400, 400, 250, 250);
|
||||
shape1->fill(255, 255, 0, 255);
|
||||
canvas->push(move(shape1));
|
||||
|
||||
//Create a Scene
|
||||
auto scene = tvg::Scene::gen();
|
||||
scene->opacity(127); //Apply opacity to scene (0 - 255)
|
||||
scene->reserve(2);
|
||||
|
||||
//Star
|
||||
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);
|
||||
scene->push(move(shape2));
|
||||
|
||||
//Circle
|
||||
auto shape3 = 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);
|
||||
scene->push(move(shape3));
|
||||
|
||||
//Draw the Scene onto the Canvas
|
||||
canvas->push(move(scene));
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Sw Engine Test Code */
|
||||
/************************************************************************/
|
||||
|
||||
static unique_ptr<tvg::SwCanvas> swCanvas;
|
||||
|
||||
void tvgSwTest(uint32_t* buffer)
|
||||
{
|
||||
//Create a Canvas
|
||||
swCanvas = tvg::SwCanvas::gen();
|
||||
swCanvas->target(buffer, WIDTH, WIDTH, HEIGHT, tvg::SwCanvas::ARGB8888);
|
||||
|
||||
/* Push the shape into the Canvas drawing list
|
||||
When this shape is into the canvas list, the shape could update & prepare
|
||||
internal data asynchronously for coming rendering.
|
||||
Canvas keeps this shape node unless user call canvas->clear() */
|
||||
tvgDrawCmds(swCanvas.get());
|
||||
}
|
||||
|
||||
void drawSwView(void* data, Eo* obj)
|
||||
{
|
||||
if (swCanvas->draw() == tvg::Result::Success) {
|
||||
swCanvas->sync();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* GL Engine Test Code */
|
||||
/************************************************************************/
|
||||
|
||||
static unique_ptr<tvg::GlCanvas> glCanvas;
|
||||
|
||||
void initGLview(Evas_Object *obj)
|
||||
{
|
||||
static constexpr auto BPP = 4;
|
||||
|
||||
//Create a Canvas
|
||||
glCanvas = tvg::GlCanvas::gen();
|
||||
glCanvas->target(nullptr, WIDTH * BPP, WIDTH, HEIGHT);
|
||||
|
||||
/* Push the shape into the Canvas drawing list
|
||||
When this shape is into the canvas list, the shape could update & prepare
|
||||
internal data asynchronously for coming rendering.
|
||||
Canvas keeps this shape node unless user call canvas->clear() */
|
||||
tvgDrawCmds(glCanvas.get());
|
||||
}
|
||||
|
||||
void drawGLview(Evas_Object *obj)
|
||||
{
|
||||
auto gl = elm_glview_gl_api_get(obj);
|
||||
gl->glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
gl->glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
if (glCanvas->draw() == tvg::Result::Success) {
|
||||
glCanvas->sync();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Main Code */
|
||||
/************************************************************************/
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
tvg::CanvasEngine tvgEngine = tvg::CanvasEngine::Sw;
|
||||
|
||||
if (argc > 1) {
|
||||
if (!strcmp(argv[1], "gl")) tvgEngine = tvg::CanvasEngine::Gl;
|
||||
}
|
||||
|
||||
//Initialize ThorVG Engine
|
||||
if (tvgEngine == tvg::CanvasEngine::Sw) {
|
||||
cout << "tvg engine: software" << endl;
|
||||
} else {
|
||||
cout << "tvg engine: opengl" << endl;
|
||||
}
|
||||
|
||||
//Threads Count
|
||||
auto threads = std::thread::hardware_concurrency();
|
||||
|
||||
//Initialize ThorVG Engine
|
||||
if (tvg::Initializer::init(tvgEngine, threads) == tvg::Result::Success) {
|
||||
|
||||
|
||||
elm_init(argc, argv);
|
||||
|
||||
if (tvgEngine == tvg::CanvasEngine::Sw) {
|
||||
createSwView();
|
||||
} else {
|
||||
createGlView();
|
||||
}
|
||||
|
||||
elm_run();
|
||||
elm_shutdown();
|
||||
|
||||
//Terminate ThorVG Engine
|
||||
tvg::Initializer::term(tvgEngine);
|
||||
|
||||
} else {
|
||||
cout << "engine is not supported" << endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -14,6 +14,7 @@ source_file = [
|
|||
'LinearGradient.cpp',
|
||||
'MultiCanvas.cpp',
|
||||
'MultiShapes.cpp',
|
||||
'Opacity.cpp',
|
||||
'PathCopy.cpp',
|
||||
'Path.cpp',
|
||||
'RadialGradient.cpp',
|
||||
|
|
|
@ -140,7 +140,7 @@ bool GlRenderer::dispose(TVG_UNUSED const Shape& shape, void *data)
|
|||
}
|
||||
|
||||
|
||||
void* GlRenderer::prepare(const Shape& shape, void* data, TVG_UNUSED const RenderTransform* transform, vector<Composite>& compList, RenderUpdateFlag flags)
|
||||
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
|
||||
GlShape* sdata = static_cast<GlShape*>(data);
|
||||
|
|
|
@ -30,7 +30,7 @@ class GlRenderer : public RenderMethod
|
|||
public:
|
||||
Surface surface = {nullptr, 0, 0, 0};
|
||||
|
||||
void* prepare(const Shape& shape, void* data, const RenderTransform* transform, vector<Composite>& compList, RenderUpdateFlag flags) override;
|
||||
void* prepare(const Shape& shape, void* data, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flags) override;
|
||||
bool dispose(const Shape& shape, void *data) override;
|
||||
bool preRender() override;
|
||||
bool render(const Shape& shape, void *data) override;
|
||||
|
|
|
@ -37,9 +37,12 @@ struct SwTask : Task
|
|||
SwSurface* surface = nullptr;
|
||||
RenderUpdateFlag flags = RenderUpdateFlag::None;
|
||||
vector<Composite> compList;
|
||||
uint32_t opacity;
|
||||
|
||||
void run() override
|
||||
{
|
||||
if (opacity == 0) return; //Invisible
|
||||
|
||||
//Valid Stroking?
|
||||
uint8_t strokeAlpha = 0;
|
||||
auto strokeWidth = sdata->strokeWidth();
|
||||
|
@ -51,12 +54,13 @@ struct SwTask : Task
|
|||
|
||||
//Invisiable shape turned to visible by alpha.
|
||||
auto prepareShape = false;
|
||||
if (!shapePrepared(&shape) && (flags & RenderUpdateFlag::Color)) prepareShape = true;
|
||||
if (!shapePrepared(&shape) && ((flags & RenderUpdateFlag::Color) || (opacity > 0))) prepareShape = true;
|
||||
|
||||
//Shape
|
||||
if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform) || prepareShape) {
|
||||
uint8_t alpha = 0;
|
||||
sdata->fillColor(nullptr, nullptr, nullptr, &alpha);
|
||||
alpha = static_cast<uint8_t>(static_cast<uint32_t>(alpha) * opacity / 255);
|
||||
bool renderShape = (alpha > 0 || sdata->fill());
|
||||
if (renderShape || strokeAlpha) {
|
||||
shapeReset(&shape);
|
||||
|
@ -181,12 +185,15 @@ bool SwRenderer::render(const Shape& shape, void *data)
|
|||
|
||||
uint8_t r, g, b, a;
|
||||
if (auto fill = task->sdata->fill()) {
|
||||
//FIXME: pass opacity to apply gradient fill?
|
||||
rasterGradientShape(surface, &task->shape, fill->id());
|
||||
} else{
|
||||
task->sdata->fillColor(&r, &g, &b, &a);
|
||||
a = static_cast<uint8_t>((task->opacity * (uint32_t) a) / 255);
|
||||
if (a > 0) rasterSolidShape(surface, &task->shape, r, g, b, a);
|
||||
}
|
||||
task->sdata->strokeColor(&r, &g, &b, &a);
|
||||
a = static_cast<uint8_t>((task->opacity * (uint32_t) a) / 255);
|
||||
if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a);
|
||||
|
||||
return true;
|
||||
|
@ -207,7 +214,7 @@ bool SwRenderer::dispose(TVG_UNUSED const Shape& sdata, void *data)
|
|||
}
|
||||
|
||||
|
||||
void* SwRenderer::prepare(const Shape& sdata, void* data, const RenderTransform* transform, vector<Composite>& compList, RenderUpdateFlag flags)
|
||||
void* SwRenderer::prepare(const Shape& sdata, void* data, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flags)
|
||||
{
|
||||
//prepare task
|
||||
auto task = static_cast<SwTask*>(data);
|
||||
|
@ -237,6 +244,7 @@ void* SwRenderer::prepare(const Shape& sdata, void* data, const RenderTransform*
|
|||
task->transform = nullptr;
|
||||
}
|
||||
|
||||
task->opacity = opacity;
|
||||
task->surface = surface;
|
||||
task->flags = flags;
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace tvg
|
|||
class SwRenderer : public RenderMethod
|
||||
{
|
||||
public:
|
||||
void* prepare(const Shape& shape, void* data, const RenderTransform* transform, vector<Composite>& compList, RenderUpdateFlag flags) override;
|
||||
void* prepare(const Shape& shape, void* data, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flags) override;
|
||||
bool dispose(const Shape& shape, void *data) override;
|
||||
bool preRender() override;
|
||||
bool postRender() override;
|
||||
|
|
|
@ -81,11 +81,11 @@ struct Canvas::Impl
|
|||
|
||||
//Update single paint node
|
||||
if (paint) {
|
||||
paint->pImpl->update(*renderer, nullptr, compList, RenderUpdateFlag::None);
|
||||
paint->pImpl->update(*renderer, nullptr, 255, compList, RenderUpdateFlag::None);
|
||||
//Update all retained paint nodes
|
||||
} else {
|
||||
for (auto paint: paints) {
|
||||
paint->pImpl->update(*renderer, nullptr, compList, RenderUpdateFlag::None);
|
||||
paint->pImpl->update(*renderer, nullptr, 255, compList, RenderUpdateFlag::None);
|
||||
}
|
||||
}
|
||||
return Result::Success;
|
||||
|
|
|
@ -67,20 +67,39 @@ Result Paint::transform(const Matrix& m) noexcept
|
|||
return Result::FailedAllocation;
|
||||
}
|
||||
|
||||
|
||||
Result Paint::bounds(float* x, float* y, float* w, float* h) const noexcept
|
||||
{
|
||||
if (pImpl->bounds(x, y, w, h)) return Result::Success;
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
|
||||
Paint* Paint::duplicate() const noexcept
|
||||
{
|
||||
return pImpl->duplicate();
|
||||
}
|
||||
|
||||
|
||||
Result Paint::composite(std::unique_ptr<Paint> target, CompositeMethod method) const noexcept
|
||||
{
|
||||
if (pImpl->composite(target.release(), method)) return Result::Success;
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
|
||||
Result Paint::opacity(uint8_t o) noexcept
|
||||
{
|
||||
if (pImpl->opacity == o) return Result::Success;
|
||||
|
||||
pImpl->opacity = o;
|
||||
pImpl->flag |= RenderUpdateFlag::Color;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
uint8_t Paint::opacity() const noexcept
|
||||
{
|
||||
return pImpl->opacity;
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace tvg
|
|||
virtual ~StrategyMethod(){}
|
||||
|
||||
virtual bool dispose(RenderMethod& renderer) = 0;
|
||||
virtual void* update(RenderMethod& renderer, const RenderTransform* transform, vector<Composite> compList, RenderUpdateFlag pFlag) = 0; //Return engine data if it has.
|
||||
virtual void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, vector<Composite> compList, RenderUpdateFlag pFlag) = 0; //Return engine data if it has.
|
||||
virtual bool render(RenderMethod& renderer) = 0;
|
||||
virtual bool bounds(float* x, float* y, float* w, float* h) const = 0;
|
||||
virtual Paint* duplicate() = 0;
|
||||
|
@ -48,6 +48,8 @@ namespace tvg
|
|||
Paint* compTarget = nullptr;
|
||||
CompositeMethod compMethod = CompositeMethod::None;
|
||||
|
||||
uint8_t opacity = 255;
|
||||
|
||||
~Impl() {
|
||||
if (smethod) delete(smethod);
|
||||
if (rTransform) delete(rTransform);
|
||||
|
@ -127,7 +129,7 @@ namespace tvg
|
|||
return smethod->dispose(renderer);
|
||||
}
|
||||
|
||||
void* update(RenderMethod& renderer, const RenderTransform* pTransform, vector<Composite>& compList, uint32_t pFlag)
|
||||
void* update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t opacity, vector<Composite>& compList, uint32_t pFlag)
|
||||
{
|
||||
if (flag & RenderUpdateFlag::Transform) {
|
||||
if (!rTransform) return nullptr;
|
||||
|
@ -140,20 +142,21 @@ namespace tvg
|
|||
void *compdata = nullptr;
|
||||
|
||||
if (compTarget && compMethod == CompositeMethod::ClipPath) {
|
||||
compdata = compTarget->pImpl->update(renderer, pTransform, compList, pFlag);
|
||||
compdata = compTarget->pImpl->update(renderer, pTransform, opacity, compList, pFlag);
|
||||
if (compdata) compList.push_back({compdata, compMethod});
|
||||
}
|
||||
|
||||
void *edata = nullptr;
|
||||
auto newFlag = static_cast<RenderUpdateFlag>(pFlag | flag);
|
||||
flag = RenderUpdateFlag::None;
|
||||
opacity = (opacity * this->opacity) / 255;
|
||||
|
||||
if (rTransform && pTransform) {
|
||||
RenderTransform outTransform(pTransform, rTransform);
|
||||
edata = smethod->update(renderer, &outTransform, compList, newFlag);
|
||||
edata = smethod->update(renderer, &outTransform, opacity, compList, newFlag);
|
||||
} else {
|
||||
auto outTransform = pTransform ? pTransform : rTransform;
|
||||
edata = smethod->update(renderer, outTransform, compList, newFlag);
|
||||
edata = smethod->update(renderer, outTransform, opacity, compList, newFlag);
|
||||
}
|
||||
|
||||
if (compdata) compList.pop_back();
|
||||
|
@ -179,6 +182,8 @@ namespace tvg
|
|||
}
|
||||
}
|
||||
|
||||
ret->pImpl->opacity = opacity;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -210,9 +215,9 @@ namespace tvg
|
|||
return inst->dispose(renderer);
|
||||
}
|
||||
|
||||
void* update(RenderMethod& renderer, const RenderTransform* transform, vector<Composite> compList, RenderUpdateFlag flag) override
|
||||
void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, vector<Composite> compList, RenderUpdateFlag flag) override
|
||||
{
|
||||
return inst->update(renderer, transform, compList, flag);
|
||||
return inst->update(renderer, transform, opacity, compList, flag);
|
||||
}
|
||||
|
||||
bool render(RenderMethod& renderer) override
|
||||
|
|
|
@ -58,13 +58,13 @@ struct Picture::Impl
|
|||
}
|
||||
|
||||
|
||||
void* update(RenderMethod &renderer, const RenderTransform* transform, vector<Composite>& compList, RenderUpdateFlag flag)
|
||||
void* update(RenderMethod &renderer, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flag)
|
||||
{
|
||||
reload();
|
||||
|
||||
if (!paint) return nullptr;
|
||||
|
||||
return paint->pImpl->update(renderer, transform, compList, flag);
|
||||
return paint->pImpl->update(renderer, transform, opacity, compList, flag);
|
||||
}
|
||||
|
||||
bool render(RenderMethod &renderer)
|
||||
|
|
|
@ -65,7 +65,7 @@ class RenderMethod
|
|||
{
|
||||
public:
|
||||
virtual ~RenderMethod() {}
|
||||
virtual void* prepare(TVG_UNUSED const Shape& shape, TVG_UNUSED void* data, TVG_UNUSED const RenderTransform* transform, TVG_UNUSED vector<Composite>& compList, TVG_UNUSED RenderUpdateFlag flags) { return nullptr; }
|
||||
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 bool dispose(TVG_UNUSED const Shape& shape, 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; }
|
||||
|
|
|
@ -44,14 +44,14 @@ struct Scene::Impl
|
|||
return true;
|
||||
}
|
||||
|
||||
void* update(RenderMethod &renderer, const RenderTransform* transform, vector<Composite>& compList, RenderUpdateFlag flag)
|
||||
void* update(RenderMethod &renderer, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flag)
|
||||
{
|
||||
/* FXIME: it requires to return list of childr engine data
|
||||
This is necessary for scene composition */
|
||||
void* edata = nullptr;
|
||||
|
||||
for (auto paint: paints) {
|
||||
edata = paint->pImpl->update(renderer, transform, compList, static_cast<uint32_t>(flag));
|
||||
edata = paint->pImpl->update(renderer, transform, opacity, compList, static_cast<uint32_t>(flag));
|
||||
}
|
||||
return edata;
|
||||
}
|
||||
|
|
|
@ -222,9 +222,9 @@ struct Shape::Impl
|
|||
return renderer.render(*shape, edata);
|
||||
}
|
||||
|
||||
void* update(RenderMethod& renderer, const RenderTransform* transform, vector<Composite>& compList, RenderUpdateFlag pFlag)
|
||||
void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag pFlag)
|
||||
{
|
||||
this->edata = renderer.prepare(*shape, this->edata, transform, compList, static_cast<RenderUpdateFlag>(pFlag | flag));
|
||||
this->edata = renderer.prepare(*shape, this->edata, transform, opacity, compList, static_cast<RenderUpdateFlag>(pFlag | flag));
|
||||
flag = RenderUpdateFlag::None;
|
||||
return this->edata;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue