mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 13:43:43 +00:00
SwRender & tvgPaint: Implement ClipPath feature (#68)
common sw_engine: Implement ClipPath feature Paint object can composite by using composite API. ClipPath composite is clipping by path unit of paint. The following cases are supported. Shape->composite(Shape); Scene->composite(Shape); Picture->composite(Shape); Add enum enum CompMethod { None = 0, ClipPath }; Add APIs Result composite(std::unique_ptr<Paint> comp, CompMethod method) const noexcept; * Example: Added testClipPath
This commit is contained in:
parent
740c59debd
commit
c70d1b1e45
17 changed files with 455 additions and 36 deletions
|
@ -56,6 +56,7 @@ enum class TVG_EXPORT PathCommand { Close = 0, MoveTo, LineTo, CubicTo };
|
|||
enum class TVG_EXPORT StrokeCap { Square = 0, Round, Butt };
|
||||
enum class TVG_EXPORT StrokeJoin { Bevel = 0, Round, Miter };
|
||||
enum class TVG_EXPORT FillSpread { Pad = 0, Reflect, Repeat };
|
||||
enum class TVG_EXPORT CompMethod { None = 0, ClipPath };
|
||||
enum class TVG_EXPORT CanvasEngine { Sw = (1 << 1), Gl = (1 << 2)};
|
||||
|
||||
|
||||
|
@ -93,6 +94,8 @@ public:
|
|||
Result bounds(float* x, float* y, float* w, float* h) const noexcept;
|
||||
Paint* duplicate() const noexcept;
|
||||
|
||||
Result composite(std::unique_ptr<Paint> target, CompMethod method) const noexcept;
|
||||
|
||||
_TVG_DECLARE_ACCESSOR();
|
||||
_TVG_DECLARE_PRIVATE(Paint);
|
||||
};
|
||||
|
|
|
@ -26,6 +26,7 @@ source_file = [
|
|||
'testSvg.cpp',
|
||||
'testTransform.cpp',
|
||||
'testUpdate.cpp',
|
||||
'testClipPath.cpp',
|
||||
]
|
||||
|
||||
foreach current_file : source_file
|
||||
|
|
217
src/examples/testClipPath.cpp
Normal file
217
src/examples/testClipPath.cpp
Normal file
|
@ -0,0 +1,217 @@
|
|||
#include "testCommon.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Drawing Commands */
|
||||
/************************************************************************/
|
||||
|
||||
void tvgDrawStar(tvg::Shape* star)
|
||||
{
|
||||
star->moveTo(199, 34);
|
||||
star->lineTo(253, 143);
|
||||
star->lineTo(374, 160);
|
||||
star->lineTo(287, 244);
|
||||
star->lineTo(307, 365);
|
||||
star->lineTo(199, 309);
|
||||
star->lineTo(97, 365);
|
||||
star->lineTo(112, 245);
|
||||
star->lineTo(26, 161);
|
||||
star->lineTo(146, 143);
|
||||
star->close();
|
||||
}
|
||||
|
||||
void tvgDrawCmds(tvg::Canvas* canvas)
|
||||
{
|
||||
if (!canvas) return;
|
||||
//Background
|
||||
auto shape = tvg::Shape::gen();
|
||||
shape->appendRect(0, 0, WIDTH, HEIGHT, 0, 0);
|
||||
shape->fill(255, 255, 255, 255);
|
||||
if (canvas->push(move(shape)) != tvg::Result::Success) return;
|
||||
|
||||
//////////////////////////////////////////////
|
||||
auto scene = tvg::Scene::gen();
|
||||
scene->reserve(2);
|
||||
|
||||
auto star1 = tvg::Shape::gen();
|
||||
tvgDrawStar(star1.get());
|
||||
star1->fill(255, 255, 0, 255);
|
||||
star1->stroke(255 ,0, 0, 128);
|
||||
star1->stroke(10);
|
||||
|
||||
//Move Star1
|
||||
star1->translate(-10, -10);
|
||||
|
||||
auto clipStar = tvg::Shape::gen();
|
||||
clipStar->appendCircle(200, 230, 110, 110);
|
||||
clipStar->fill(255, 255, 255, 255); // clip object must have alpha.
|
||||
clipStar->translate(10, 10);
|
||||
|
||||
star1->composite(move(clipStar), tvg::CompMethod::ClipPath);
|
||||
|
||||
auto star2 = tvg::Shape::gen();
|
||||
tvgDrawStar(star2.get());
|
||||
star2->fill(0, 255, 255, 64);
|
||||
star2->stroke(0 ,255, 0, 128);
|
||||
star2->stroke(10);
|
||||
|
||||
//Move Star2
|
||||
star2->translate(10, 40);
|
||||
|
||||
auto clip = tvg::Shape::gen();
|
||||
clip->appendCircle(200, 230, 130, 130);
|
||||
clip->fill(255, 255, 255, 255); // clip object must have alpha.
|
||||
clip->translate(10, 10);
|
||||
|
||||
scene->push(move(star1));
|
||||
scene->push(move(star2));
|
||||
|
||||
//Clipping scene to shape
|
||||
scene->composite(move(clip), tvg::CompMethod::ClipPath);
|
||||
|
||||
canvas->push(move(scene));
|
||||
|
||||
//////////////////////////////////////////////
|
||||
auto star3 = tvg::Shape::gen();
|
||||
tvgDrawStar(star3.get());
|
||||
star3->translate(400, 0);
|
||||
star3->fill(255, 255, 0, 255); //r, g, b, a
|
||||
star3->stroke(255 ,0, 0, 128);
|
||||
star3->stroke(10);
|
||||
|
||||
auto clipRect = tvg::Shape::gen();
|
||||
clipRect->appendRect(480, 110, 200, 200, 0, 0); //x, y, w, h, rx, ry
|
||||
clipRect->fill(255, 255, 255, 255); // clip object must have alpha.
|
||||
clipRect->translate(20, 20);
|
||||
|
||||
//Clipping scene to rect(shape)
|
||||
star3->composite(move(clipRect), tvg::CompMethod::ClipPath);
|
||||
|
||||
canvas->push(move(star3));
|
||||
|
||||
//////////////////////////////////////////////
|
||||
auto picture = tvg::Picture::gen();
|
||||
char buf[PATH_MAX];
|
||||
sprintf(buf,"%s/cartman.svg", EXAMPLE_DIR);
|
||||
|
||||
if (picture->load(buf) != tvg::Result::Success) return;
|
||||
|
||||
picture->scale(3);
|
||||
picture->translate(200, 400);
|
||||
|
||||
auto clipPath = tvg::Shape::gen();
|
||||
clipPath->appendCircle(350, 510, 110, 110); //x, y, w, h, rx, ry
|
||||
clipPath->appendCircle(350, 650, 50, 50); //x, y, w, h, rx, ry
|
||||
clipPath->fill(255, 255, 255, 255); // clip object must have alpha.
|
||||
clipPath->translate(20, 20);
|
||||
|
||||
//Clipping picture to path
|
||||
picture->composite(move(clipPath), tvg::CompMethod::ClipPath);
|
||||
|
||||
canvas->push(move(picture));
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Sw Engine Test Code */
|
||||
/************************************************************************/
|
||||
|
||||
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;
|
||||
}
|
|
@ -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, RenderUpdateFlag flags)
|
||||
void* GlRenderer::prepare(const Shape& shape, void* data, TVG_UNUSED const RenderTransform* transform, 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, RenderUpdateFlag flags) override;
|
||||
void* prepare(const Shape& shape, void* data, const RenderTransform* transform, 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;
|
||||
|
|
|
@ -270,7 +270,7 @@ SwFixed mathMean(SwFixed angle1, SwFixed angle2);
|
|||
void shapeReset(SwShape* shape);
|
||||
bool shapeGenOutline(SwShape* shape, const Shape* sdata, const Matrix* transform);
|
||||
bool shapePrepare(SwShape* shape, const Shape* sdata, const SwSize& clip, const Matrix* transform);
|
||||
bool shapeGenRle(SwShape* shape, const Shape* sdata, const SwSize& clip, bool antiAlias);
|
||||
bool shapeGenRle(SwShape* shape, const Shape* sdata, const SwSize& clip, bool antiAlias, bool hasComposite);
|
||||
void shapeDelOutline(SwShape* shape);
|
||||
void shapeResetStroke(SwShape* shape, const Shape* sdata, const Matrix* transform);
|
||||
bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwSize& clip);
|
||||
|
@ -293,6 +293,9 @@ void fillFetchRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x,
|
|||
|
||||
SwRleData* rleRender(const SwOutline* outline, const SwBBox& bbox, const SwSize& clip, bool antiAlias);
|
||||
void rleFree(SwRleData* rle);
|
||||
void rleClipPath(SwRleData *rle, const SwRleData *clip);
|
||||
void rleClipRect(SwRleData *rle, const SwBBox* clip);
|
||||
|
||||
|
||||
bool rasterCompositor(SwSurface* surface);
|
||||
bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id);
|
||||
|
|
|
@ -36,6 +36,7 @@ struct SwTask : Task
|
|||
Matrix* transform = nullptr;
|
||||
SwSurface* surface = nullptr;
|
||||
RenderUpdateFlag flags = RenderUpdateFlag::None;
|
||||
vector<Composite> compList;
|
||||
|
||||
void run() override
|
||||
{
|
||||
|
@ -58,10 +59,11 @@ struct SwTask : Task
|
|||
if (!shapePrepare(&shape, sdata, clip, transform)) return;
|
||||
if (renderShape) {
|
||||
auto antiAlias = (strokeAlpha > 0 && strokeWidth >= 2) ? false : true;
|
||||
if (!shapeGenRle(&shape, sdata, clip, antiAlias)) return;
|
||||
if (!shapeGenRle(&shape, sdata, clip, antiAlias, compList.size() > 0 ? true : false)) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Fill
|
||||
if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform)) {
|
||||
auto fill = sdata->fill();
|
||||
|
@ -82,6 +84,20 @@ struct SwTask : Task
|
|||
shapeDelStroke(&shape);
|
||||
}
|
||||
}
|
||||
|
||||
//Composite clip-path
|
||||
for (auto comp : compList) {
|
||||
SwShape *compShape = &static_cast<SwTask*>(comp.edata)->shape;
|
||||
if (comp.method == CompMethod::ClipPath) {
|
||||
//Clip to fill(path) rle
|
||||
if (shape.rle && compShape->rect) rleClipRect(shape.rle, &compShape->bbox);
|
||||
else if (shape.rle && compShape->rle) rleClipPath(shape.rle, compShape->rle);
|
||||
|
||||
//Clip to stroke rle
|
||||
if (shape.strokeRle && compShape->rect) rleClipRect(shape.strokeRle, &compShape->bbox);
|
||||
else if (shape.strokeRle && compShape->rle) rleClipPath(shape.strokeRle, compShape->rle);
|
||||
}
|
||||
}
|
||||
shapeDelOutline(&shape);
|
||||
}
|
||||
};
|
||||
|
@ -184,7 +200,7 @@ bool SwRenderer::dispose(TVG_UNUSED const Shape& sdata, void *data)
|
|||
}
|
||||
|
||||
|
||||
void* SwRenderer::prepare(const Shape& sdata, void* data, const RenderTransform* transform, RenderUpdateFlag flags)
|
||||
void* SwRenderer::prepare(const Shape& sdata, void* data, const RenderTransform* transform, vector<Composite>& compList, RenderUpdateFlag flags)
|
||||
{
|
||||
//prepare task
|
||||
auto task = static_cast<SwTask*>(data);
|
||||
|
@ -196,6 +212,10 @@ void* SwRenderer::prepare(const Shape& sdata, void* data, const RenderTransform*
|
|||
if (flags == RenderUpdateFlag::None || task->valid()) return task;
|
||||
|
||||
task->sdata = &sdata;
|
||||
if (compList.size() > 0) {
|
||||
for (auto comp : compList) static_cast<SwTask*>(comp.edata)->get();
|
||||
task->compList.assign(compList.begin(), compList.end());
|
||||
}
|
||||
|
||||
if (transform) {
|
||||
if (!task->transform) task->transform = static_cast<Matrix*>(malloc(sizeof(Matrix)));
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace tvg
|
|||
class SwRenderer : public RenderMethod
|
||||
{
|
||||
public:
|
||||
void* prepare(const Shape& shape, void* data, const RenderTransform* transform, RenderUpdateFlag flags) override;
|
||||
void* prepare(const Shape& shape, void* data, const RenderTransform* transform, vector<Composite>& compList, RenderUpdateFlag flags) override;
|
||||
bool dispose(const Shape& shape, void *data) override;
|
||||
bool preRender() override;
|
||||
bool postRender() override;
|
||||
|
|
|
@ -613,6 +613,95 @@ static int _genRle(RleWorker& rw)
|
|||
}
|
||||
|
||||
|
||||
SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *targetRle, SwSpan *outSpans, uint32_t spanCnt)
|
||||
{
|
||||
auto out = outSpans;
|
||||
auto spans = targetRle->spans;
|
||||
auto end = targetRle->spans + targetRle->size;
|
||||
auto clipSpans = clip->spans;
|
||||
auto clipEnd = clip->spans + clip->size;
|
||||
|
||||
while (spanCnt && spans < end ) {
|
||||
if (clipSpans > clipEnd) {
|
||||
spans = end;
|
||||
break;
|
||||
}
|
||||
if (clipSpans->y > spans->y) {
|
||||
++spans;
|
||||
continue;
|
||||
}
|
||||
if (spans->y != clipSpans->y) {
|
||||
++clipSpans;
|
||||
continue;
|
||||
}
|
||||
auto sx1 = spans->x;
|
||||
auto sx2 = sx1 + spans->len;
|
||||
auto cx1 = clipSpans->x;
|
||||
auto cx2 = cx1 + clipSpans->len;
|
||||
|
||||
if (cx1 < sx1 && cx2 < sx1) {
|
||||
++clipSpans;
|
||||
continue;
|
||||
}
|
||||
else if (sx1 < cx1 && sx2 < cx1) {
|
||||
++spans;
|
||||
continue;
|
||||
}
|
||||
auto x = sx1 > cx1 ? sx1 : cx1;
|
||||
auto len = (sx2 < cx2 ? sx2 : cx2) - x;
|
||||
if (len) {
|
||||
auto spansCorverage = spans->coverage;
|
||||
auto clipSpansCoverage = clipSpans->coverage;
|
||||
out->x = sx1 > cx1 ? sx1 : cx1;
|
||||
out->len = (sx2 < cx2 ? sx2 : cx2) - out->x;
|
||||
out->y = spans->y;
|
||||
out->coverage = (uint8_t)((spansCorverage * clipSpansCoverage) >> 8);
|
||||
++out;
|
||||
--spanCnt;
|
||||
}
|
||||
if (sx2 < cx2) ++spans;
|
||||
else ++clipSpans;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRle, SwSpan *outSpans, uint32_t spanCnt)
|
||||
{
|
||||
auto out = outSpans;
|
||||
auto spans = targetRle->spans;
|
||||
auto end = targetRle->spans + targetRle->size;
|
||||
auto minx = static_cast<int16_t>(bbox->min.x);
|
||||
auto miny = static_cast<int16_t>(bbox->min.y);
|
||||
auto maxx = minx + static_cast<int16_t>(bbox->max.x - bbox->min.x) - 1;
|
||||
auto maxy = miny + static_cast<int16_t>(bbox->max.y - bbox->min.y) - 1;
|
||||
|
||||
while (spanCnt && spans < end ) {
|
||||
if (spans->y > maxy) {
|
||||
spans = end;
|
||||
break;
|
||||
}
|
||||
if (spans->y < miny || spans->x > maxx || spans->x + spans->len <= minx) {
|
||||
++spans;
|
||||
continue;
|
||||
}
|
||||
if (spans->x < minx) {
|
||||
out->len = (spans->len - (minx - spans->x)) < (maxx - minx + 1) ? (spans->len - (minx - spans->x)) : (maxx - minx + 1);
|
||||
out->x = minx;
|
||||
}
|
||||
else {
|
||||
out->x = spans->x;
|
||||
out->len = spans->len < (maxx - spans->x + 1) ? spans->len : (maxx - spans->x + 1);
|
||||
}
|
||||
if (out->len != 0) {
|
||||
out->y = spans->y;
|
||||
out->coverage = spans->coverage;
|
||||
++out;
|
||||
}
|
||||
++spans;
|
||||
--spanCnt;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
|
@ -747,3 +836,47 @@ void rleFree(SwRleData* rle)
|
|||
if (rle->spans) free(rle->spans);
|
||||
free(rle);
|
||||
}
|
||||
|
||||
void updateRleSpans(SwRleData *rle, SwSpan* curSpans, uint32_t size)
|
||||
{
|
||||
if (!rle->spans || !curSpans || size == 0) return;
|
||||
rle->size = size;
|
||||
rle->spans = static_cast<SwSpan*>(realloc(rle->spans, rle->size * sizeof(SwSpan)));
|
||||
|
||||
if (!rle->spans) return;
|
||||
|
||||
for (int i = 0; i < (int)rle->size ; i++)
|
||||
{
|
||||
rle->spans[i].x = curSpans[i].x;
|
||||
rle->spans[i].y = curSpans[i].y;
|
||||
rle->spans[i].len = curSpans[i].len;
|
||||
rle->spans[i].coverage = curSpans[i].coverage;
|
||||
}
|
||||
}
|
||||
|
||||
void rleClipPath(SwRleData *rle, const SwRleData *clip)
|
||||
{
|
||||
if (rle->size == 0 || clip->size == 0) return;
|
||||
auto spanCnt = rle->size > clip->size ? rle->size : clip->size;
|
||||
auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (spanCnt)));
|
||||
if (!spans) return;
|
||||
auto spansEnd = _intersectSpansRegion(clip, rle, spans, spanCnt);
|
||||
|
||||
//Update Spans
|
||||
updateRleSpans(rle, spans, spansEnd - spans);
|
||||
|
||||
if (spans) free(spans);
|
||||
}
|
||||
|
||||
void rleClipRect(SwRleData *rle, const SwBBox* clip)
|
||||
{
|
||||
if (rle->size == 0) return;
|
||||
auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (rle->size)));
|
||||
if (!spans) return;
|
||||
auto spansEnd = _intersectSpansRect(clip, rle, spans, rle->size);
|
||||
|
||||
//Update Spans
|
||||
updateRleSpans(rle, spans, spansEnd - spans);
|
||||
|
||||
if (spans) free(spans);
|
||||
}
|
||||
|
|
|
@ -459,14 +459,14 @@ bool shapePrepare(SwShape* shape, const Shape* sdata, const SwSize& clip, const
|
|||
}
|
||||
|
||||
|
||||
bool shapeGenRle(SwShape* shape, TVG_UNUSED const Shape* sdata, const SwSize& clip, bool antiAlias)
|
||||
bool shapeGenRle(SwShape* shape, TVG_UNUSED const Shape* sdata, const SwSize& clip, bool antiAlias, bool hasComposite)
|
||||
{
|
||||
//FIXME: Should we draw it?
|
||||
//Case: Stroke Line
|
||||
//if (shape.outline->opened) return true;
|
||||
|
||||
//Case A: Fast Track Rectangle Drawing
|
||||
if ((shape->rect = _fastTrack(shape->outline))) return true;
|
||||
if (!hasComposite && (shape->rect = _fastTrack(shape->outline))) return true;
|
||||
//Case B: Normale Shape RLE Drawing
|
||||
if ((shape->rle = rleRender(shape->outline, shape->bbox, clip, antiAlias))) return true;
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ struct Canvas::Impl
|
|||
paint->pImpl->dispose(*renderer);
|
||||
delete(paint);
|
||||
}
|
||||
|
||||
paints.clear();
|
||||
|
||||
return Result::Success;
|
||||
|
@ -73,15 +74,17 @@ struct Canvas::Impl
|
|||
{
|
||||
if (!renderer) return Result::InsufficientCondition;
|
||||
|
||||
vector<Composite> compList;
|
||||
|
||||
//Update single paint node
|
||||
if (paint) {
|
||||
if (!paint->pImpl->update(*renderer, nullptr, RenderUpdateFlag::None)) {
|
||||
if (!paint->pImpl->update(*renderer, nullptr, compList, nullptr, RenderUpdateFlag::None)) {
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
//Update retained all paint nodes
|
||||
} else {
|
||||
for (auto paint: paints) {
|
||||
if (!paint->pImpl->update(*renderer, nullptr, RenderUpdateFlag::None)) {
|
||||
if (!paint->pImpl->update(*renderer, nullptr, compList, nullptr, RenderUpdateFlag::None)) {
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,7 +67,6 @@ 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;
|
||||
|
@ -78,3 +77,10 @@ Paint* Paint::duplicate() const noexcept
|
|||
{
|
||||
return pImpl->duplicate();
|
||||
}
|
||||
|
||||
Result Paint::composite(std::unique_ptr<Paint> target, CompMethod method) const noexcept
|
||||
{
|
||||
if (pImpl->composite(target.release(), method)) return Result::Success;
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace tvg
|
|||
virtual ~StrategyMethod(){}
|
||||
|
||||
virtual bool dispose(RenderMethod& renderer) = 0;
|
||||
virtual bool update(RenderMethod& renderer, const RenderTransform* transform, RenderUpdateFlag pFlag) = 0;
|
||||
virtual bool update(RenderMethod& renderer, const RenderTransform* transform, vector<Composite> compList, void** edata, RenderUpdateFlag pFlag) = 0;
|
||||
virtual bool render(RenderMethod& renderer) = 0;
|
||||
virtual bool bounds(float* x, float* y, float* w, float* h) const = 0;
|
||||
virtual Paint* duplicate() = 0;
|
||||
|
@ -45,6 +45,9 @@ namespace tvg
|
|||
RenderTransform *rTransform = nullptr;
|
||||
uint32_t flag = RenderUpdateFlag::None;
|
||||
|
||||
Paint* compTarget = nullptr;
|
||||
CompMethod compMethod = CompMethod::None;
|
||||
|
||||
~Impl() {
|
||||
if (smethod) delete(smethod);
|
||||
if (rTransform) delete(rTransform);
|
||||
|
@ -120,10 +123,11 @@ namespace tvg
|
|||
|
||||
bool dispose(RenderMethod& renderer)
|
||||
{
|
||||
if (this->compTarget) this->compTarget->pImpl->dispose(renderer);
|
||||
return smethod->dispose(renderer);
|
||||
}
|
||||
|
||||
bool update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t pFlag)
|
||||
bool update(RenderMethod& renderer, const RenderTransform* pTransform, vector<Composite>& compList, void **edata, uint32_t pFlag)
|
||||
{
|
||||
if (flag & RenderUpdateFlag::Transform) {
|
||||
if (!rTransform) return false;
|
||||
|
@ -135,14 +139,30 @@ namespace tvg
|
|||
|
||||
auto newFlag = static_cast<RenderUpdateFlag>(pFlag | flag);
|
||||
flag = RenderUpdateFlag::None;
|
||||
bool updated = false;
|
||||
void *compEngineData = nullptr; //composite target paint's engine data.
|
||||
|
||||
if (this->compTarget && compMethod == CompMethod::ClipPath) {
|
||||
if (this->compTarget->pImpl->update(renderer, pTransform, compList, &compEngineData, static_cast<RenderUpdateFlag>(pFlag | flag))) {
|
||||
Composite comp;
|
||||
comp.edata = compEngineData;
|
||||
comp.method = this->compMethod;
|
||||
compList.push_back(comp);
|
||||
}
|
||||
}
|
||||
|
||||
if (rTransform && pTransform) {
|
||||
RenderTransform outTransform(pTransform, rTransform);
|
||||
return smethod->update(renderer, &outTransform, newFlag);
|
||||
updated = smethod->update(renderer, &outTransform, compList, edata, newFlag);
|
||||
} else {
|
||||
auto outTransform = pTransform ? pTransform : rTransform;
|
||||
return smethod->update(renderer, outTransform, newFlag);
|
||||
updated = smethod->update(renderer, outTransform, compList, edata, newFlag);
|
||||
}
|
||||
|
||||
if (compEngineData) {
|
||||
compList.pop_back();
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
bool render(RenderMethod& renderer)
|
||||
|
@ -165,6 +185,14 @@ namespace tvg
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool composite(Paint* target, CompMethod compMethod)
|
||||
{
|
||||
this->compTarget = target;
|
||||
this->compMethod = compMethod;
|
||||
if (this->compTarget) return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -186,9 +214,9 @@ namespace tvg
|
|||
return inst->dispose(renderer);
|
||||
}
|
||||
|
||||
bool update(RenderMethod& renderer, const RenderTransform* transform, RenderUpdateFlag flag) override
|
||||
bool update(RenderMethod& renderer, const RenderTransform* transform, vector<Composite> compList, void** edata, RenderUpdateFlag flag) override
|
||||
{
|
||||
return inst->update(renderer, transform, flag);
|
||||
return inst->update(renderer, transform, compList, edata, flag);
|
||||
}
|
||||
|
||||
bool render(RenderMethod& renderer) override
|
||||
|
|
|
@ -57,13 +57,13 @@ struct Picture::Impl
|
|||
}
|
||||
|
||||
|
||||
bool update(RenderMethod &renderer, const RenderTransform* transform, RenderUpdateFlag flag)
|
||||
bool update(RenderMethod &renderer, const RenderTransform* transform, vector<Composite>& compList, void** edata, RenderUpdateFlag flag)
|
||||
{
|
||||
reload();
|
||||
|
||||
if (!paint) return false;
|
||||
|
||||
return paint->pImpl->update(renderer, transform, flag);
|
||||
return paint->pImpl->update(renderer, transform, compList, edata, flag);
|
||||
}
|
||||
|
||||
bool render(RenderMethod &renderer)
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#ifndef _TVG_RENDER_H_
|
||||
#define _TVG_RENDER_H_
|
||||
|
||||
#include <vector>
|
||||
#include "tvgCommon.h"
|
||||
|
||||
namespace tvg
|
||||
|
@ -36,6 +37,11 @@ struct Surface
|
|||
uint32_t cs;
|
||||
};
|
||||
|
||||
struct Composite {
|
||||
void* edata;
|
||||
CompMethod method;
|
||||
};
|
||||
|
||||
enum RenderUpdateFlag {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, All = 32};
|
||||
|
||||
struct RenderTransform
|
||||
|
@ -59,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 RenderUpdateFlag flags) { return nullptr; }
|
||||
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 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,10 +44,10 @@ struct Scene::Impl
|
|||
return true;
|
||||
}
|
||||
|
||||
bool update(RenderMethod &renderer, const RenderTransform* transform, RenderUpdateFlag flag)
|
||||
bool update(RenderMethod &renderer, const RenderTransform* transform, vector<Composite>& compList, void** edata, RenderUpdateFlag flag)
|
||||
{
|
||||
for (auto paint: paints) {
|
||||
if (!paint->pImpl->update(renderer, transform, static_cast<uint32_t>(flag))) return false;
|
||||
if (!paint->pImpl->update(renderer, transform, compList, edata, static_cast<uint32_t>(flag))) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -221,13 +221,12 @@ struct Shape::Impl
|
|||
return renderer.render(*shape, edata);
|
||||
}
|
||||
|
||||
bool update(RenderMethod& renderer, const RenderTransform* transform, RenderUpdateFlag pFlag)
|
||||
bool update(RenderMethod& renderer, const RenderTransform* transform, vector<Composite>& compList, void** edata, RenderUpdateFlag pFlag)
|
||||
{
|
||||
edata = renderer.prepare(*shape, edata, transform, static_cast<RenderUpdateFlag>(pFlag | flag));
|
||||
|
||||
this->edata = renderer.prepare(*shape, this->edata, transform, compList, static_cast<RenderUpdateFlag>(pFlag | flag));
|
||||
flag = RenderUpdateFlag::None;
|
||||
|
||||
if (edata) return true;
|
||||
if (edata) *edata = this->edata;
|
||||
if (this->edata) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue