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);
}
+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;
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;
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);
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);
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);
/************************************************************************/
-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);
}
}
-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;
}
}
-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;
}
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
}
-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;
{
//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);
//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);
//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;
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;
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.
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()) {
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;
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;
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;
}
}
+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;
surface->h = h;
surface->cs = cs;
+ vport.x = vport.y = 0;
+ vport.w = surface->w;
+ vport.h = surface->h;
+
return rasterCompositor(surface);
}
return true;
}
+
RenderRegion SwRenderer::region(RenderData data)
{
return static_cast<SwTask*>(data)->bounds();
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);
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;
SwSurface* surface = nullptr; //active surface
Array<SwTask*> tasks; //async task list
Array<SwSurface*> compositors; //render targets cache list
+ RenderRegion vport; //viewport
SwRenderer(){};
~SwRenderer();
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) */
//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;
//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
/* 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) {
{
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;
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;
/* 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;
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;
/* 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;
}
//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;
}
}
-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;
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) {
#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 };
}
}
+ /* 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;
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;
}
{
//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*;
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;