diff --git a/inc/thorvg.h b/inc/thorvg.h index 96bb9b6f..38ad4723 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -57,7 +57,7 @@ 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 FillRule { Winding = 0, EvenOdd }; -enum class TVG_EXPORT CompositeMethod { None = 0, ClipPath }; +enum class TVG_EXPORT CompositeMethod { None = 0, ClipPath, AlphaMask, InvAlphaMask }; enum class TVG_EXPORT CanvasEngine { Sw = (1 << 1), Gl = (1 << 2)}; diff --git a/src/examples/Masking.cpp b/src/examples/Masking.cpp new file mode 100644 index 00000000..31e32f10 --- /dev/null +++ b/src/examples/Masking.cpp @@ -0,0 +1,153 @@ +#include "Common.h" +#include + +/************************************************************************/ +/* Drawing Commands */ +/************************************************************************/ + +uint32_t *data = nullptr; + +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; + + string path(EXAMPLE_DIR"/rawimage_200x300.raw"); + + ifstream file(path); + if (!file.is_open()) return ; + data = (uint32_t*)malloc(sizeof(uint32_t) * (200*300)); + file.read(reinterpret_cast(data), sizeof (data) * 200 * 300); + file.close(); + + auto picture = tvg::Picture::gen(); + if (picture->load(data, 200, 300, true) != tvg::Result::Success) return; + picture->translate(400, 250); + + //Alpha mask + auto mask = tvg::Shape::gen(); + mask->appendCircle(500, 350, 75, 75); + mask->fill(0, 0, 0, 50); + if (canvas->push(move(picture)) != tvg::Result::Success) return; + + auto cover = tvg::Shape::gen(); + cover->appendRect(400, 250, 200, 300, 0, 0); + cover->fill(255, 255, 0, 255); + cover->composite(move(mask), tvg::CompositeMethod::AlphaMask); + if (canvas->push(move(cover)) != tvg::Result::Success) return; + +} + + +/************************************************************************/ +/* Sw Engine Test Code */ +/************************************************************************/ + +static unique_ptr 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 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(tvg::CanvasEngine::Sw); + + if (data) free(data); + + } else { + cout << "engine is not supported" << endl; + } + return 0; +} diff --git a/src/examples/meson.build b/src/examples/meson.build index 1385a749..e616877d 100644 --- a/src/examples/meson.build +++ b/src/examples/meson.build @@ -31,6 +31,7 @@ source_file = [ 'Transform.cpp', 'Update.cpp', 'ClipPath.cpp', + 'Masking.cpp', ] foreach current_file : source_file diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index 3b14f5e4..8ae73957 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -312,6 +312,7 @@ void rleFree(SwRleData* rle); void rleReset(SwRleData* rle); void rleClipPath(SwRleData *rle, const SwRleData *clip); void rleClipRect(SwRleData *rle, const SwBBox* clip); +void rleAlphaMask(SwRleData *rle, const SwRleData *clip); bool mpoolInit(uint32_t threads); bool mpoolTerm(); diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index bc6b23d8..17a334ca 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -144,7 +144,11 @@ struct SwShapeTask : SwTask if (shape.strokeRle) { if (compShape->rect) rleClipRect(shape.strokeRle, &compShape->bbox); else if (compShape->rle) rleClipPath(shape.strokeRle, compShape->rle); - } + } + } else if ((*comp).method == CompositeMethod::AlphaMask) { + rleAlphaMask(shape.rle, compShape->rle); + } else if (comp.method == CompositeMethod::InvAlphaMask) { + // TODO } } end: @@ -188,8 +192,12 @@ struct SwImageTask : SwTask auto compShape = &static_cast((*comp).edata)->shape; if (compShape->rect) rleClipRect(image.rle, &compShape->bbox); else if (compShape->rle) rleClipPath(image.rle, compShape->rle); + } else if ((*comp).method == CompositeMethod::AlphaMask) { + rleAlphaMask(image.rle, compShape->rle); + } else if ((*comp).method == CompositeMethod::InvAlphaMask) { + // TODO } - } + } } } } diff --git a/src/lib/sw_engine/tvgSwRle.cpp b/src/lib/sw_engine/tvgSwRle.cpp index eb309735..c53db097 100644 --- a/src/lib/sw_engine/tvgSwRle.cpp +++ b/src/lib/sw_engine/tvgSwRle.cpp @@ -665,6 +665,75 @@ SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *targetRle, return out; } +SwSpan* _intersectMaskRegion(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 clipSpans1 = clip->spans; + auto clipEnd = clip->spans + clip->size; + + auto maskClipMin = clipSpans1->y; + auto maskClipMax = clipSpans1->y; + + while (clipSpans1->y) { + if (clipSpans1->y > maskClipMax) + maskClipMax = clipSpans1->y; + + if (clipSpans1->y < maskClipMax) + maskClipMin = clipSpans1->y; + clipSpans1++; + } + + while (spanCnt && spans < end ) { + if (clipSpans > clipEnd) { + spans = end; + break; + } + + + if (spans->y < maskClipMin || spans->y > maskClipMax) { + out->x = spans->x; + out->y = spans->y; + out->len = spans->len; + out->coverage = spans->coverage; + ++out; + } + else { + while (clipSpans->y) { + auto sx1 = spans->x; + auto sx2 = sx1 + spans->len; + auto cx1 = clipSpans->x; + auto cx2 = cx1 + clipSpans->len; + auto x = sx1 > cx1 ? sx1 : cx1; + auto len = (sx2 < cx2 ? sx2 : cx2) - x; + + if (len > 1) { + out->x = sx1; + out->y = clipSpans->y; + out->len = cx1-sx1; + out->coverage = spans->coverage; + ++out; + + out->x = cx2; + out->y = clipSpans->y; + out->len = sx2 - cx2; + out->coverage = spans->coverage; + ++out; + } + clipSpans++; + } + } + --spanCnt; + ++spans; + } + + return out; +} + + SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRle, SwSpan *outSpans, uint32_t spanCnt) { auto out = outSpans; @@ -889,3 +958,21 @@ void rleClipRect(SwRleData *rle, const SwBBox* clip) if (spans) free(spans); } + + +void rleAlphaMask(SwRleData *rle, const SwRleData *clip) +{ + if (rle->size == 0 || clip->size == 0) return; + auto spanCnt = rle->size + clip->size; + + auto spans = static_cast(malloc(sizeof(SwSpan) * (spanCnt))); + + if (!spans) return; + auto spansEnd = _intersectMaskRegion(clip, rle, spans, spanCnt); + + //Update Spans + updateRleSpans(rle, spans, spansEnd - spans); + + if (spans) free(spans); +} + diff --git a/src/lib/tvgPaint.h b/src/lib/tvgPaint.h index 7c01a722..4d2a7213 100644 --- a/src/lib/tvgPaint.h +++ b/src/lib/tvgPaint.h @@ -149,7 +149,7 @@ namespace tvg void *compdata = nullptr; - if (compTarget && compMethod == CompositeMethod::ClipPath) { + if (compTarget && (compMethod != CompositeMethod::None)) { compdata = compTarget->pImpl->update(renderer, pTransform, opacity, compList, pFlag); if (compdata) compList.push({compdata, compMethod}); }