From 1cffdbff65e893ee0d421441701fb39738dcc98a Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Fri, 26 Mar 2021 16:59:52 +0900 Subject: [PATCH] common sw_engine: optimize single rectangle ClipPath. If ClipPath is a singular rectangle, we don't need to apply this to all children nodes to adjust rle span regions. Rather than its regular sequence, we can adjust render region as merging viewport that is introduced internally, All in all, If a Paint has a single ClipPath that is Rectangle, it sets viewport with Rectangle area that viewport is applied to raster engine to cut off the rendering boundary. In the normal case it brings trivial effects. but when use SVGs which has a viewbox, it could increase the performance up to 10% (profiled with 200 svgs rendering at the same time) Note that, this won't be applied if the Paint has affine or rotation transform. @Issues: 294 --- src/examples/ClipPath.cpp | 2 +- src/lib/gl_engine/tvgGlRenderer.cpp | 13 +++++++ src/lib/gl_engine/tvgGlRenderer.h | 2 + src/lib/sw_engine/tvgSwCommon.h | 12 +++--- src/lib/sw_engine/tvgSwImage.cpp | 14 ++----- src/lib/sw_engine/tvgSwMath.cpp | 24 ++++++------ src/lib/sw_engine/tvgSwRaster.cpp | 23 ++--------- src/lib/sw_engine/tvgSwRenderer.cpp | 43 +++++++++++++++++---- src/lib/sw_engine/tvgSwRenderer.h | 3 ++ src/lib/sw_engine/tvgSwRle.cpp | 29 +++++++------- src/lib/sw_engine/tvgSwShape.cpp | 22 +++++++---- src/lib/tvgPaint.h | 76 +++++++++++++++++++++++++++++++++++-- src/lib/tvgRender.h | 8 ++-- 13 files changed, 187 insertions(+), 84 deletions(-) diff --git a/src/examples/ClipPath.cpp b/src/examples/ClipPath.cpp index a780b38..bfe81ef 100644 --- a/src/examples/ClipPath.cpp +++ b/src/examples/ClipPath.cpp @@ -111,7 +111,7 @@ void tvgDrawCmds(tvg::Canvas* canvas) star3->translate(400, 0); auto clipRect = tvg::Shape::gen(); - clipRect->appendRect(480, 110, 200, 200, 0, 0); //x, y, w, h, rx, ry + clipRect->appendRect(500, 120, 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); diff --git a/src/lib/gl_engine/tvgGlRenderer.cpp b/src/lib/gl_engine/tvgGlRenderer.cpp index cbc13de..40f6787 100644 --- a/src/lib/gl_engine/tvgGlRenderer.cpp +++ b/src/lib/gl_engine/tvgGlRenderer.cpp @@ -234,6 +234,19 @@ RenderData GlRenderer::prepare(const Shape& shape, RenderData data, const Render } +RenderRegion GlRenderer::viewport() +{ + return {0, 0, UINT32_MAX, UINT32_MAX}; +} + + +bool GlRenderer::viewport(TVG_UNUSED const RenderRegion& vp) +{ + //TODO: + return true; +} + + int GlRenderer::init(uint32_t threads) { if ((initEngineCnt++) > 0) return true; diff --git a/src/lib/gl_engine/tvgGlRenderer.h b/src/lib/gl_engine/tvgGlRenderer.h index 7e288b7..0c0ecac 100644 --- a/src/lib/gl_engine/tvgGlRenderer.h +++ b/src/lib/gl_engine/tvgGlRenderer.h @@ -38,6 +38,8 @@ public: bool postRender() override; bool dispose(RenderData data) override;; RenderRegion region(RenderData data) override; + RenderRegion viewport() override; + bool viewport(const RenderRegion& vp) override; bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h); bool sync() override; diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index 0b80081..d30bf43 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -296,16 +296,16 @@ SwFixed mathLength(const SwPoint& pt); bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut); SwFixed mathMean(SwFixed angle1, SwFixed angle2); SwPoint mathTransform(const Point* to, const Matrix* transform); -bool mathUpdateOutlineBBox(const SwOutline* outline, SwBBox& bbox, const SwSize& clip); +bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion); void shapeReset(SwShape* shape); bool shapeGenOutline(SwShape* shape, const Shape* sdata, unsigned tid, const Matrix* transform); -bool shapePrepare(SwShape* shape, const Shape* sdata, unsigned tid, const SwSize& clip, const Matrix* transform, SwBBox& bbox); +bool shapePrepare(SwShape* shape, const Shape* sdata, unsigned tid, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion); bool shapePrepared(const SwShape* shape); bool shapeGenRle(SwShape* shape, const Shape* sdata, bool antiAlias, bool hasComposite); void shapeDelOutline(SwShape* shape, uint32_t tid); void shapeResetStroke(SwShape* shape, const Shape* sdata, const Matrix* transform); -bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, unsigned tid, const Matrix* transform, const SwSize& clip, SwBBox& bbox); +bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, unsigned tid, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion); void shapeFree(SwShape* shape); void shapeDelStroke(SwShape* shape); bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable); @@ -320,9 +320,9 @@ bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline); SwOutline* strokeExportOutline(SwStroke* stroke, unsigned tid); void strokeFree(SwStroke* stroke); -bool imagePrepare(SwImage* image, const Picture* pdata, unsigned tid, const SwSize& clip, const Matrix* transform, SwBBox& bbox); +bool imagePrepare(SwImage* image, const Picture* pdata, unsigned tid, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion); bool imagePrepared(const SwImage* image); -bool imageGenRle(SwImage* image, TVG_UNUSED const Picture* pdata, const SwBBox& bbox, bool antiAlias, bool hasComposite); +bool imageGenRle(SwImage* image, TVG_UNUSED const Picture* pdata, const SwBBox& renderRegion, bool antiAlias); void imageDelOutline(SwImage* image, uint32_t tid); void imageReset(SwImage* image); bool imageGenOutline(SwImage* image, const Picture* pdata, unsigned tid, const Matrix* transform); @@ -334,7 +334,7 @@ void fillFree(SwFill* fill); void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t offset, uint32_t len); void fillFetchRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len); -SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& bbox, bool antiAlias); +SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias); void rleFree(SwRleData* rle); void rleReset(SwRleData* rle); void rleClipPath(SwRleData *rle, const SwRleData *clip); diff --git a/src/lib/sw_engine/tvgSwImage.cpp b/src/lib/sw_engine/tvgSwImage.cpp index 80a139f..03bf174 100644 --- a/src/lib/sw_engine/tvgSwImage.cpp +++ b/src/lib/sw_engine/tvgSwImage.cpp @@ -33,16 +33,10 @@ /************************************************************************/ -bool imagePrepare(SwImage* image, const Picture* pdata, unsigned tid, const SwSize& clip, const Matrix* transform, SwBBox& bbox) +bool imagePrepare(SwImage* image, const Picture* pdata, unsigned tid, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion) { if (!imageGenOutline(image, pdata, tid, transform)) return false; - if (!mathUpdateOutlineBBox(image->outline, bbox, clip)) return false; - - //Guarantee boundary from mathUpdateOutlineBBox() - bbox.min.x = max(bbox.min.x, TO_SWCOORD(0)); - bbox.min.y = max(bbox.min.y, TO_SWCOORD(0)); - - return true; + return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion); } @@ -52,9 +46,9 @@ bool imagePrepared(const SwImage* image) } -bool imageGenRle(SwImage* image, TVG_UNUSED const Picture* pdata, const SwBBox& bbox, bool antiAlias, TVG_UNUSED bool hasComposite) +bool imageGenRle(SwImage* image, TVG_UNUSED const Picture* pdata, const SwBBox& renderRegion, bool antiAlias) { - if ((image->rle = rleRender(image->rle, image->outline, bbox, antiAlias))) return true; + if ((image->rle = rleRender(image->rle, image->outline, renderRegion, antiAlias))) return true; return false; } diff --git a/src/lib/sw_engine/tvgSwMath.cpp b/src/lib/sw_engine/tvgSwMath.cpp index 03f9cbf..77c8b8f 100644 --- a/src/lib/sw_engine/tvgSwMath.cpp +++ b/src/lib/sw_engine/tvgSwMath.cpp @@ -429,14 +429,14 @@ SwPoint mathTransform(const Point* to, const Matrix* transform) } -bool mathUpdateOutlineBBox(const SwOutline* outline, SwBBox& bbox, const SwSize& clip) +bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion) { if (!outline) return false; auto pt = outline->pts; if (outline->ptsCnt == 0 || outline->cntrsCnt <= 0) { - bbox.reset(); + renderRegion.reset(); return false; } @@ -453,20 +453,22 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, SwBBox& bbox, const SwSize& if (yMin > pt->y) yMin = pt->y; if (yMax < pt->y) yMax = pt->y; } - bbox.min.x = xMin >> 6; - bbox.max.x = (xMax + 63) >> 6; - bbox.min.y = yMin >> 6; - bbox.max.y = (yMax + 63) >> 6; + renderRegion.min.x = xMin >> 6; + renderRegion.max.x = (xMax + 63) >> 6; + renderRegion.min.y = yMin >> 6; + renderRegion.max.y = (yMax + 63) >> 6; - //Guarantee surface boundary - bbox.max.x = min(bbox.max.x, clip.w); - bbox.max.y = min(bbox.max.y, clip.h); + renderRegion.max.x = min(renderRegion.max.x, clipRegion.max.x); + renderRegion.max.y = min(renderRegion.max.y, clipRegion.max.y); + renderRegion.min.x = max(renderRegion.min.x, clipRegion.min.x); + renderRegion.min.y = max(renderRegion.min.y, clipRegion.min.y); //Check valid region - if (bbox.max.x - bbox.min.x < 1 && bbox.max.y - bbox.min.y < 1) return false; + if (renderRegion.max.x - renderRegion.min.x < 1 && renderRegion.max.y - renderRegion.min.y < 1) return false; //Check boundary - if (bbox.min.x >= clip.w || bbox.min.y >= clip.h || bbox.max.x <= 0 || bbox.max.y <= 0) return false; + if (renderRegion.min.x >= clipRegion.max.x || renderRegion.min.y >= clipRegion.max.y || + renderRegion.max.x <= clipRegion.min.x || renderRegion.max.y <= clipRegion.min.y) return false; return true; } \ No newline at end of file diff --git a/src/lib/sw_engine/tvgSwRaster.cpp b/src/lib/sw_engine/tvgSwRaster.cpp index f5cfaa9..1564f5d 100644 --- a/src/lib/sw_engine/tvgSwRaster.cpp +++ b/src/lib/sw_engine/tvgSwRaster.cpp @@ -86,19 +86,6 @@ static bool _identify(const Matrix* transform) } -static SwBBox _clipRegion(const Surface* surface, const SwBBox& in) -{ - auto bbox = in; - - if (bbox.min.x < 0) bbox.min.x = 0; - if (bbox.min.y < 0) bbox.min.y = 0; - if (bbox.max.x > static_cast(surface->w)) bbox.max.x = surface->w; - if (bbox.max.y > static_cast(surface->h)) bbox.max.y = surface->h; - - return bbox; -} - - static bool _translucent(const SwSurface* surface, uint8_t a) { if (a < 255) return true; @@ -799,9 +786,8 @@ bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id) { //Fast Track if (shape->rect) { - auto region = _clipRegion(surface, shape->bbox); - if (id == FILL_ID_LINEAR) return _rasterLinearGradientRect(surface, region, shape->fill); - return _rasterRadialGradientRect(surface, region, shape->fill); + if (id == FILL_ID_LINEAR) return _rasterLinearGradientRect(surface, shape->bbox, shape->fill); + return _rasterRadialGradientRect(surface, shape->bbox, shape->fill); } else { if (id == FILL_ID_LINEAR) return _rasterLinearGradientRle(surface, shape->rle, shape->fill); return _rasterRadialGradientRle(surface, shape->rle, shape->fill); @@ -821,9 +807,8 @@ bool rasterSolidShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, //Fast Track if (shape->rect) { - auto region = _clipRegion(surface, shape->bbox); - if (translucent) return _rasterTranslucentRect(surface, region, color); - return _rasterSolidRect(surface, region, color); + if (translucent) return _rasterTranslucentRect(surface, shape->bbox, color); + return _rasterSolidRect(surface, shape->bbox, color); } if (translucent) { return _rasterTranslucentRle(surface, shape->rle, color); diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index 549f674..de908c3 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -47,7 +47,6 @@ struct SwTask : Task //Range over? region.x = bbox.min.x > 0 ? bbox.min.x : 0; region.y = bbox.min.y > 0 ? bbox.min.y : 0; - region.w = bbox.max.x - region.x; region.h = bbox.max.y - region.y; @@ -79,8 +78,7 @@ struct SwShapeTask : SwTask sdata->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha); } bool validStroke = (strokeAlpha > 0) || sdata->strokeFill(); - - SwSize clip = {static_cast(surface->w), static_cast(surface->h)}; + auto clipRegion = bbox; //invisible shape turned to visible by alpha. auto prepareShape = false; @@ -94,7 +92,7 @@ struct SwShapeTask : SwTask bool renderShape = (alpha > 0 || sdata->fill()); if (renderShape || validStroke) { shapeReset(&shape); - if (!shapePrepare(&shape, sdata, tid, clip, transform, bbox)) goto err; + if (!shapePrepare(&shape, sdata, tid, transform, clipRegion, bbox)) goto err; if (renderShape) { /* We assume that if stroke width is bigger than 2, shape outline below stroke could be full covered by stroke drawing. @@ -123,7 +121,7 @@ struct SwShapeTask : SwTask if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) { if (validStroke) { shapeResetStroke(&shape, sdata, transform); - if (!shapeGenStrokeRle(&shape, sdata, tid, transform, clip, bbox)) goto err; + if (!shapeGenStrokeRle(&shape, sdata, tid, transform, clipRegion, bbox)) goto err; ++addStroking; if (auto fill = sdata->strokeFill()) { @@ -177,7 +175,7 @@ struct SwImageTask : SwTask void run(unsigned tid) override { - SwSize clip = {static_cast(surface->w), static_cast(surface->h)}; + auto clipRegion = bbox; //Invisible shape turned to visible by alpha. auto prepareImage = false; @@ -185,11 +183,11 @@ struct SwImageTask : SwTask if (prepareImage) { imageReset(&image); - if (!imagePrepare(&image, pdata, tid, clip, transform, bbox)) goto end; + if (!imagePrepare(&image, pdata, tid, transform, clipRegion, bbox)) goto end; //Clip Path? if (clips.count > 0) { - if (!imageGenRle(&image, pdata, bbox, false, true)) goto end; + if (!imageGenRle(&image, pdata, bbox, false)) goto end; if (image.rle) { for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) { auto clipper = &static_cast(*clip)->shape; @@ -241,6 +239,12 @@ bool SwRenderer::clear() for (auto task = tasks.data; task < (tasks.data + tasks.count); ++task) (*task)->done(); tasks.clear(); + if (surface) { + vport.x = vport.y = 0; + vport.w = surface->w; + vport.h = surface->h; + } + return true; } @@ -251,6 +255,20 @@ bool SwRenderer::sync() } +RenderRegion SwRenderer::viewport() +{ + return vport; +} + + +bool SwRenderer::viewport(const RenderRegion& vp) +{ + this->vport = vp; + + 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; @@ -266,6 +284,10 @@ bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t surface->h = h; surface->cs = cs; + vport.x = vport.y = 0; + vport.w = surface->w; + vport.h = surface->h; + return rasterCompositor(surface); } @@ -348,6 +370,7 @@ bool SwRenderer::renderShape(RenderData data) return true; } + RenderRegion SwRenderer::region(RenderData data) { return static_cast(data)->bounds(); @@ -514,6 +537,10 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, task->opacity = opacity; task->surface = surface; task->flags = flags; + task->bbox.min.x = max(static_cast(0), static_cast(vport.x)); + task->bbox.min.y = max(static_cast(0), static_cast(vport.y)); + task->bbox.max.x = min(static_cast(surface->w), static_cast(vport.x + vport.w)); + task->bbox.max.y = min(static_cast(surface->h), static_cast(vport.y + vport.h)); tasks.push(task); TaskScheduler::request(task); diff --git a/src/lib/sw_engine/tvgSwRenderer.h b/src/lib/sw_engine/tvgSwRenderer.h index 999db0d..ef8b558 100644 --- a/src/lib/sw_engine/tvgSwRenderer.h +++ b/src/lib/sw_engine/tvgSwRenderer.h @@ -42,6 +42,8 @@ public: bool postRender() override; bool dispose(RenderData data) override; RenderRegion region(RenderData data) override; + RenderRegion viewport() override; + bool viewport(const RenderRegion& vp) override; bool clear() override; bool sync() override; @@ -59,6 +61,7 @@ private: SwSurface* surface = nullptr; //active surface Array tasks; //async task list Array compositors; //render targets cache list + RenderRegion vport; //viewport SwRenderer(){}; ~SwRenderer(); diff --git a/src/lib/sw_engine/tvgSwRle.cpp b/src/lib/sw_engine/tvgSwRle.cpp index 5c4719a..ec8e35e 100644 --- a/src/lib/sw_engine/tvgSwRle.cpp +++ b/src/lib/sw_engine/tvgSwRle.cpp @@ -159,7 +159,7 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor y += rw.cellMin.y; //Clip Y range - if (y < 0 || y >= rw.cellMax.y) return; + if (y < rw.cellMin.y || y >= rw.cellMax.y) return; /* compute the coverage line's coverage, depending on the outline fill rule */ /* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */ @@ -198,14 +198,14 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor //Clip x range SwCoord xOver = 0; if (x + acount >= rw.cellMax.x) xOver -= (x + acount - rw.cellMax.x); - if (x < 0) xOver += x; + if (x < rw.cellMin.x) xOver -= (rw.cellMin.x - x); //span->len += (acount + xOver) - 1; span->len += (acount + xOver); return; } - if (count >= MAX_SPANS) { + if (count >= MAX_SPANS) { _genSpan(rw.rle, rw.spans, count); rw.spansCnt = 0; rw.ySpan = 0; @@ -217,9 +217,9 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor //Clip x range SwCoord xOver = 0; if (x + acount >= rw.cellMax.x) xOver -= (x + acount - rw.cellMax.x); - if (x < 0) { - xOver += x; - x = 0; + if (x < rw.cellMin.x) { + xOver -= (rw.cellMin.x - x); + x = rw.cellMin.x; } //Nothing to draw @@ -317,11 +317,10 @@ static void _setCell(RleWorker& rw, SwPoint pos) /* All cells that are on the left of the clipping region go to the min_ex - 1 horizontal position. */ + pos.x -= rw.cellMin.x; pos.y -= rw.cellMin.y; if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x; - pos.x -= rw.cellMin.x; - if (pos.x < 0) pos.x = -1; //Are we moving to a different cell? if (pos != rw.cellPos) { @@ -340,7 +339,6 @@ static void _startCell(RleWorker& rw, SwPoint pos) { if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x; if (pos.x < rw.cellMin.x) pos.x = rw.cellMin.x; - //if (pos.x < rw.cellMin.x) pos.x = (rw.cellMin.x - 1); rw.area = 0; rw.cover = 0; @@ -373,10 +371,9 @@ static void _lineTo(RleWorker& rw, const SwPoint& to) auto e2 = TRUNC(to); //vertical clipping - if ((e1.y >= rw.cellMax.y && e2.y >= rw.cellMax.y) || - (e1.y < rw.cellMin.y && e2.y < rw.cellMin.y)) { - rw.pos = to; - return; + if ((e1.y >= rw.cellMax.y && e2.y >= rw.cellMax.y) || (e1.y < rw.cellMin.y && e2.y < rw.cellMin.y)) { + rw.pos = to; + return; } auto diff = to - rw.pos; @@ -782,7 +779,7 @@ void _replaceClipSpan(SwRleData *rle, SwSpan* clippedSpans, uint32_t size) /* External Class Implementation */ /************************************************************************/ -SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& bbox, bool antiAlias) +SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias) { constexpr auto RENDER_POOL_SIZE = 16384L; constexpr auto BAND_SIZE = 40; @@ -801,8 +798,8 @@ SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& bbo rw.area = 0; rw.cover = 0; rw.invalid = true; - rw.cellMin = bbox.min; - rw.cellMax = bbox.max; + rw.cellMin = renderRegion.min; + rw.cellMax = renderRegion.max; rw.cellXCnt = rw.cellMax.x - rw.cellMin.x; rw.cellYCnt = rw.cellMax.y - rw.cellMin.y; rw.ySpan = 0; diff --git a/src/lib/sw_engine/tvgSwShape.cpp b/src/lib/sw_engine/tvgSwShape.cpp index cd951dd..4dcb91c 100644 --- a/src/lib/sw_engine/tvgSwShape.cpp +++ b/src/lib/sw_engine/tvgSwShape.cpp @@ -367,12 +367,20 @@ bool _fastTrack(const SwOutline* outline) /* External Class Implementation */ /************************************************************************/ -bool shapePrepare(SwShape* shape, const Shape* sdata, unsigned tid, const SwSize& clip, const Matrix* transform, SwBBox& bbox) +bool shapePrepare(SwShape* shape, const Shape* sdata, unsigned tid, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion) { if (!shapeGenOutline(shape, sdata, tid, transform)) return false; - if (!mathUpdateOutlineBBox(shape->outline, shape->bbox, clip)) return false; + if (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion)) return false; - bbox = shape->bbox; + //Keep it for Rasterization Region + shape->bbox = renderRegion; + + //Check valid region + if (renderRegion.max.x - renderRegion.min.x < 1 && renderRegion.max.y - renderRegion.min.y < 1) return false; + + //Check boundary + if (renderRegion.min.x >= clipRegion.max.x || renderRegion.min.y >= clipRegion.max.y || + renderRegion.max.x <= clipRegion.min.x || renderRegion.max.y <= clipRegion.min.y) return false; return true; } @@ -393,7 +401,7 @@ bool shapeGenRle(SwShape* shape, TVG_UNUSED const Shape* sdata, bool antiAlias, //Case A: Fast Track Rectangle Drawing if (!hasComposite && (shape->rect = _fastTrack(shape->outline))) return true; //Case B: Normale Shape RLE Drawing - if ((shape->rle = rleRender(shape->rle, shape->outline, shape->bbox, antiAlias))) return true; + if ((shape->rle = rleRender(shape->rle, shape->outline, shape->bbox,antiAlias))) return true; return false; } @@ -535,7 +543,7 @@ void shapeResetStroke(SwShape* shape, const Shape* sdata, const Matrix* transfor } -bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, unsigned tid, const Matrix* transform, const SwSize& clip, SwBBox& bbox) +bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, unsigned tid, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion) { SwOutline* shapeOutline = nullptr; SwOutline* strokeOutline = nullptr; @@ -566,12 +574,12 @@ bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, unsigned tid, const M goto fail; } - if (!mathUpdateOutlineBBox(strokeOutline, bbox, clip)) { + if (!mathUpdateOutlineBBox(strokeOutline, clipRegion, renderRegion)) { ret = false; goto fail; } - shape->strokeRle = rleRender(shape->strokeRle, strokeOutline, bbox, true); + shape->strokeRle = rleRender(shape->strokeRle, strokeOutline, renderRegion, true); fail: if (freeOutline) { diff --git a/src/lib/tvgPaint.h b/src/lib/tvgPaint.h index 464de43..7ba2d16 100644 --- a/src/lib/tvgPaint.h +++ b/src/lib/tvgPaint.h @@ -26,6 +26,58 @@ #include #include "tvgRender.h" +static inline bool FLT_SAME(float a, float b) +{ + return (fabsf(a - b) < FLT_EPSILON); +} + +static bool _clipPathFastTrack(Paint* cmpTarget, const RenderTransform* transform, RenderRegion& viewport) +{ + /* Access Shape class by Paint is bad... but it's ok still it's an internal usage. */ + auto shape = static_cast(cmpTarget); + + //Rectangle Candidates? + const Point* pts; + if (shape->pathCoords(&pts) != 4) return false; + + //No Rotation? + if (transform) { + if (transform->m.e12 != 0 || transform->m.e21 != 0 || transform->m.e11 != transform->m.e22) return false; + } + + //Othogonal Rectangle? + auto pt1 = pts + 0; + auto pt2 = pts + 1; + auto pt3 = pts + 2; + auto pt4 = pts + 3; + + if ((FLT_SAME(pt1->x, pt2->x) && FLT_SAME(pt2->y, pt3->y) && FLT_SAME(pt3->x, pt4->x) && FLT_SAME(pt1->y, pt4->y)) || + (FLT_SAME(pt2->x, pt3->x) && FLT_SAME(pt1->y, pt2->y) && FLT_SAME(pt1->x, pt4->x) && FLT_SAME(pt3->y, pt4->y))) { + + auto x1 = pt1->x; + auto y1 = pt1->y; + auto x2 = pt3->x; + auto y2 = pt3->y; + + if (transform) { + x1 = x1 * transform->m.e11 + transform->m.e13; + y1 = y1 * transform->m.e22 + transform->m.e23; + x2 = x2 * transform->m.e11 + transform->m.e13; + y2 = y2 * transform->m.e22 + transform->m.e23; + } + + viewport.x = static_cast(x1); + viewport.y = static_cast(y1); + viewport.w = static_cast(roundf(x2 - x1 + 0.5f)); + viewport.h = static_cast(roundf(y2 - y1 + 0.5f)); + + return true; + } + + return false; +} + + namespace tvg { enum class PaintType { Shape = 0, Scene, Picture }; @@ -150,13 +202,29 @@ namespace tvg } } + /* 1. Composition Pre Processing */ void *cmpData = nullptr; + RenderRegion viewport; + bool cmpFastTrack = false; if (cmpTarget) { - cmpData = cmpTarget->pImpl->update(renderer, pTransform, 255, clips, pFlag); - if (cmpMethod == CompositeMethod::ClipPath) clips.push(cmpData); + /* If transform has no rotation factors && ClipPath is a simple rectangle, + we can avoid regular ClipPath sequence but use viewport for performance */ + if (cmpMethod == CompositeMethod::ClipPath) { + RenderRegion viewport2; + if ((cmpFastTrack = _clipPathFastTrack(cmpTarget, pTransform, viewport2))) { + viewport = renderer.viewport(); + renderer.viewport(viewport2); + } + } + + if (!cmpFastTrack) { + cmpData = cmpTarget->pImpl->update(renderer, pTransform, 255, clips, pFlag); + if (cmpMethod == CompositeMethod::ClipPath) clips.push(cmpData); + } } + /* 2. Main Update */ void *edata = nullptr; auto newFlag = static_cast(pFlag | flag); flag = RenderUpdateFlag::None; @@ -170,7 +238,9 @@ namespace tvg edata = smethod->update(renderer, outTransform, opacity, clips, newFlag); } - if (cmpData && cmpMethod == CompositeMethod::ClipPath) clips.pop(); + /* 3. Composition Post Processing */ + if (cmpFastTrack) renderer.viewport(viewport); + else if (cmpData && cmpMethod == CompositeMethod::ClipPath) clips.pop(); return edata; } diff --git a/src/lib/tvgRender.h b/src/lib/tvgRender.h index d2cb529..7b06fb7 100644 --- a/src/lib/tvgRender.h +++ b/src/lib/tvgRender.h @@ -34,9 +34,9 @@ struct Surface { //TODO: Union for multiple types uint32_t* buffer; - uint32_t stride; - uint32_t w, h; - uint32_t cs; + uint32_t stride; + uint32_t w, h; + uint32_t cs; }; using RenderData = void*; @@ -78,6 +78,8 @@ public: virtual bool postRender() = 0; virtual bool dispose(RenderData data) = 0; virtual RenderRegion region(RenderData data) = 0; + virtual RenderRegion viewport() = 0; + virtual bool viewport(const RenderRegion& vp) = 0; virtual bool clear() = 0; virtual bool sync() = 0; -- 2.7.4