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
This commit is contained in:
Hermet Park 2021-03-26 16:59:52 +09:00 committed by Hermet Park
parent 4a8f45577a
commit f1fe36d8f6
13 changed files with 187 additions and 84 deletions

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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<SwCoord>(surface->w)) bbox.max.x = surface->w;
if (bbox.max.y > static_cast<SwCoord>(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);

View file

@ -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<SwCoord>(surface->w), static_cast<SwCoord>(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<SwCoord>(surface->w), static_cast<SwCoord>(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<SwShapeTask*>(*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<SwTask*>(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<SwCoord>(0), static_cast<SwCoord>(vport.x));
task->bbox.min.y = max(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.y));
task->bbox.max.x = min(static_cast<SwCoord>(surface->w), static_cast<SwCoord>(vport.x + vport.w));
task->bbox.max.y = min(static_cast<SwCoord>(surface->h), static_cast<SwCoord>(vport.y + vport.h));
tasks.push(task);
TaskScheduler::request(task);

View file

@ -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<SwTask*> tasks; //async task list
Array<SwSurface*> compositors; //render targets cache list
RenderRegion vport; //viewport
SwRenderer(){};
~SwRenderer();

View file

@ -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;

View file

@ -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) {

View file

@ -26,6 +26,58 @@
#include <math.h>
#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<Shape*>(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<uint32_t>(x1);
viewport.y = static_cast<uint32_t>(y1);
viewport.w = static_cast<uint32_t>(roundf(x2 - x1 + 0.5f));
viewport.h = static_cast<uint32_t>(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<RenderUpdateFlag>(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;
}

View file

@ -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;