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
Change-Id: Ie291890304230c34006fbe7f96a8707e01d9439c
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);
};
--- /dev/null
+#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;
+}
'LinearGradient.cpp',
'MultiCanvas.cpp',
'MultiShapes.cpp',
+ 'Opacity.cpp',
'PathCopy.cpp',
'Path.cpp',
'RadialGradient.cpp',
}
-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);
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;
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();
//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);
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;
}
-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);
task->transform = nullptr;
}
+ task->opacity = opacity;
task->surface = surface;
task->flags = flags;
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;
//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;
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;
+}
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;
Paint* compTarget = nullptr;
CompositeMethod compMethod = CompositeMethod::None;
+ uint8_t opacity = 255;
+
~Impl() {
if (smethod) delete(smethod);
if (rTransform) delete(rTransform);
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;
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();
}
}
+ ret->pImpl->opacity = opacity;
+
return ret;
}
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
}
- 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)
{
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; }
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;
}
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;
}