This commit is contained in:
Hermet Park 2025-06-07 20:44:49 +09:00 committed by GitHub
commit 0b991705ad
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 585 additions and 253 deletions

View file

@ -859,6 +859,12 @@ bool GlRenderer::sync()
} }
void GlRenderer::damage(TVG_UNUSED const RenderRegion& region)
{
//TODO:
}
RenderRegion GlRenderer::region(RenderData data) RenderRegion GlRenderer::region(RenderData data)
{ {
if (currentPass()->isEmpty()) return {}; if (currentPass()->isEmpty()) return {};

View file

@ -80,6 +80,7 @@ public:
bool postRender() override; bool postRender() override;
void dispose(RenderData data) override;; void dispose(RenderData data) override;;
RenderRegion region(RenderData data) override; RenderRegion region(RenderData data) override;
void damage(const RenderRegion& region) override;
RenderRegion viewport() override; RenderRegion viewport() override;
bool viewport(const RenderRegion& vp) override; bool viewport(const RenderRegion& vp) override;
bool blend(BlendMethod method) override; bool blend(BlendMethod method) override;

View file

@ -23,6 +23,7 @@
#ifndef _TVG_SW_COMMON_H_ #ifndef _TVG_SW_COMMON_H_
#define _TVG_SW_COMMON_H_ #define _TVG_SW_COMMON_H_
#include <algorithm>
#include "tvgCommon.h" #include "tvgCommon.h"
#include "tvgMath.h" #include "tvgMath.h"
#include "tvgRender.h" #include "tvgRender.h"
@ -117,22 +118,47 @@ struct SwSpan
uint16_t x, y; uint16_t x, y;
uint16_t len; uint16_t len;
uint8_t coverage; uint8_t coverage;
void fetch(const RenderRegion& bbox, int32_t& x, int32_t& len) const
{
x = std::max((int32_t)this->x, bbox.min.x);
len = std::min((int32_t)(this->x + this->len), bbox.max.x) - x;
}
}; };
struct SwRle struct SwRle
{ {
Array<SwSpan> spans; Array<SwSpan> spans;
bool invalid() const const SwSpan* fetch(const RenderRegion& bbox, const SwSpan** end) const
{ {
return spans.empty(); return fetch(bbox.min.y, bbox.max.y, end);
} }
bool valid() const const SwSpan* fetch(int32_t min, uint32_t max, const SwSpan** end) const
{ {
return !invalid(); const SwSpan* begin;
if (min <= spans.first().y) {
begin = spans.begin();
} else {
auto comp = [](const SwSpan& span, int y) { return span.y < y; };
begin = lower_bound(spans.begin(), spans.end(), min, comp);
}
if (end) {
if (max > spans.last().y) {
*end = spans.end();
} else {
auto comp = [](int y, const SwSpan& span) { return y <= span.y; };
*end = upper_bound(spans.begin(), spans.end(), max, comp);
}
}
return begin;
} }
bool invalid() const { return spans.empty(); }
bool valid() const { return !invalid(); }
uint32_t size() const { return spans.count; } uint32_t size() const { return spans.count; }
SwSpan* data() const { return spans.data; } SwSpan* data() const { return spans.data; }
}; };
@ -566,11 +592,11 @@ SwOutline* mpoolReqDashOutline(SwMpool* mpool, unsigned idx);
void mpoolRetDashOutline(SwMpool* mpool, unsigned idx); void mpoolRetDashOutline(SwMpool* mpool, unsigned idx);
bool rasterCompositor(SwSurface* surface); bool rasterCompositor(SwSurface* surface);
bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity); bool rasterGradientShape(SwSurface* surface, SwShape* shape, const RenderRegion& bbox, const Fill* fdata, uint8_t opacity);
bool rasterShape(SwSurface* surface, SwShape* shape, RenderColor& c); bool rasterShape(SwSurface* surface, SwShape* shape, const RenderRegion& bbox, RenderColor& c);
bool rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity); bool rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity);
bool rasterStroke(SwSurface* surface, SwShape* shape, RenderColor& c); bool rasterStroke(SwSurface* surface, SwShape* shape, const RenderRegion& bbox, RenderColor& c);
bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity); bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const RenderRegion& bbox, const Fill* fdata, uint8_t opacity);
bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h, pixel_t val = 0); bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h, pixel_t val = 0);
void rasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len); void rasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len);
void rasterTranslucentPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity); void rasterTranslucentPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity);

View file

@ -472,37 +472,44 @@ static bool _rasterRect(SwSurface* surface, const RenderRegion& bbox, const Rend
/* Rle */ /* Rle */
/************************************************************************/ /************************************************************************/
static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRle* rle, SwMask maskOp, uint8_t a) static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRle* rle, const RenderRegion& bbox, SwMask maskOp, uint8_t a)
{ {
auto cbuffer = surface->compositor->image.buf8; auto cbuffer = surface->compositor->image.buf8;
auto cstride = surface->compositor->image.stride; auto cstride = surface->compositor->image.stride;
const SwSpan* end;
int32_t x, len;
uint8_t src; uint8_t src;
ARRAY_FOREACH(span, rle->spans) { for (auto span = rle->fetch(bbox, &end); span < end; ++span) {
auto cmp = &cbuffer[span->y * cstride + span->x]; span->fetch(bbox, x, len);
auto cmp = &cbuffer[span->y * cstride + x];
if (span->coverage == 255) src = a; if (span->coverage == 255) src = a;
else src = MULTIPLY(a, span->coverage); else src = MULTIPLY(a, span->coverage);
auto ialpha = 255 - src; auto ialpha = 255 - src;
for (auto x = 0; x < span->len; ++x, ++cmp) { for (auto x = 0; x < len; ++x, ++cmp) {
*cmp = maskOp(src, *cmp, ialpha); *cmp = maskOp(src, *cmp, ialpha);
} }
} }
return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
} }
static bool _rasterDirectMaskedRle(SwSurface* surface, SwRle* rle, SwMask maskOp, uint8_t a) static bool _rasterDirectMaskedRle(SwSurface* surface, SwRle* rle, const RenderRegion& bbox, SwMask maskOp, uint8_t a)
{ {
auto cbuffer = surface->compositor->image.buf8; auto cbuffer = surface->compositor->image.buf8;
auto cstride = surface->compositor->image.stride; auto cstride = surface->compositor->image.stride;
const SwSpan* end;
int32_t x, len;
uint8_t src; uint8_t src;
ARRAY_FOREACH(span, rle->spans) { for (auto span = rle->fetch(bbox, &end); span < end; ++span) {
auto cmp = &cbuffer[span->y * cstride + span->x]; span->fetch(bbox, x, len);
auto dst = &surface->buf8[span->y * surface->stride + span->x]; auto cmp = &cbuffer[span->y * cstride + x];
auto dst = &surface->buf8[span->y * surface->stride + x];
if (span->coverage == 255) src = a; if (span->coverage == 255) src = a;
else src = MULTIPLY(a, span->coverage); else src = MULTIPLY(a, span->coverage);
for (auto x = 0; x < span->len; ++x, ++cmp, ++dst) { for (auto x = 0; x < len; ++x, ++cmp, ++dst) {
auto tmp = maskOp(src, *cmp, 0); //not use alpha auto tmp = maskOp(src, *cmp, 0); //not use alpha
*dst = tmp + MULTIPLY(*dst, ~tmp); *dst = tmp + MULTIPLY(*dst, ~tmp);
} }
@ -511,7 +518,7 @@ static bool _rasterDirectMaskedRle(SwSurface* surface, SwRle* rle, SwMask maskOp
} }
static bool _rasterMaskedRle(SwSurface* surface, SwRle* rle, const RenderColor& c) static bool _rasterMaskedRle(SwSurface* surface, SwRle* rle, const RenderRegion& bbox, const RenderColor& c)
{ {
TVGLOG("SW_ENGINE", "Masked(%d) Rle", (int)surface->compositor->method); TVGLOG("SW_ENGINE", "Masked(%d) Rle", (int)surface->compositor->method);
@ -519,30 +526,33 @@ static bool _rasterMaskedRle(SwSurface* surface, SwRle* rle, const RenderColor&
if (surface->channelSize != sizeof(uint8_t)) return false; if (surface->channelSize != sizeof(uint8_t)) return false;
auto maskOp = _getMaskOp(surface->compositor->method); auto maskOp = _getMaskOp(surface->compositor->method);
if (_direct(surface->compositor->method)) return _rasterDirectMaskedRle(surface, rle, maskOp, c.a); if (_direct(surface->compositor->method)) return _rasterDirectMaskedRle(surface, rle, bbox, maskOp, c.a);
else return _rasterCompositeMaskedRle(surface, rle, maskOp, c.a); else return _rasterCompositeMaskedRle(surface, rle, bbox, maskOp, c.a);
return false; return false;
} }
static bool _rasterMattedRle(SwSurface* surface, SwRle* rle, const RenderColor& c) static bool _rasterMattedRle(SwSurface* surface, SwRle* rle, const RenderRegion& bbox, const RenderColor& c)
{ {
TVGLOG("SW_ENGINE", "Matted(%d) Rle", (int)surface->compositor->method); TVGLOG("SW_ENGINE", "Matted(%d) Rle", (int)surface->compositor->method);
auto cbuffer = surface->compositor->image.buf8; auto cbuffer = surface->compositor->image.buf8;
auto csize = surface->compositor->image.channelSize; auto csize = surface->compositor->image.channelSize;
auto alpha = surface->alpha(surface->compositor->method); auto alpha = surface->alpha(surface->compositor->method);
const SwSpan* end;
int32_t x, len;
//32bit channels //32bit channels
if (surface->channelSize == sizeof(uint32_t)) { if (surface->channelSize == sizeof(uint32_t)) {
uint32_t src; uint32_t src;
auto color = surface->join(c.r, c.g, c.b, c.a); auto color = surface->join(c.r, c.g, c.b, c.a);
ARRAY_FOREACH(span, rle->spans) { for (auto span = rle->fetch(bbox, &end); span < end; ++span) {
auto dst = &surface->buf32[span->y * surface->stride + span->x]; span->fetch(bbox, x, len);
auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize]; auto dst = &surface->buf32[span->y * surface->stride + x];
auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + x) * csize];
if (span->coverage == 255) src = color; if (span->coverage == 255) src = color;
else src = ALPHA_BLEND(color, span->coverage); else src = ALPHA_BLEND(color, span->coverage);
for (uint32_t x = 0; x < span->len; ++x, ++dst, cmp += csize) { for (auto x = 0; x < len; ++x, ++dst, cmp += csize) {
auto tmp = ALPHA_BLEND(src, alpha(cmp)); auto tmp = ALPHA_BLEND(src, alpha(cmp));
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
} }
@ -550,12 +560,13 @@ static bool _rasterMattedRle(SwSurface* surface, SwRle* rle, const RenderColor&
//8bit grayscale //8bit grayscale
} else if (surface->channelSize == sizeof(uint8_t)) { } else if (surface->channelSize == sizeof(uint8_t)) {
uint8_t src; uint8_t src;
ARRAY_FOREACH(span, rle->spans) { for (auto span = rle->fetch(bbox, &end); span < end; ++span) {
auto dst = &surface->buf8[span->y * surface->stride + span->x]; span->fetch(bbox, x, len);
auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize]; auto dst = &surface->buf8[span->y * surface->stride + x];
auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + x) * csize];
if (span->coverage == 255) src = c.a; if (span->coverage == 255) src = c.a;
else src = MULTIPLY(c.a, span->coverage); else src = MULTIPLY(c.a, span->coverage);
for (uint32_t x = 0; x < span->len; ++x, ++dst, cmp += csize) { for (auto x = 0; x < len; ++x, ++dst, cmp += csize) {
*dst = INTERPOLATE8(src, *dst, alpha(cmp)); *dst = INTERPOLATE8(src, *dst, alpha(cmp));
} }
} }
@ -564,20 +575,23 @@ static bool _rasterMattedRle(SwSurface* surface, SwRle* rle, const RenderColor&
} }
static bool _rasterBlendingRle(SwSurface* surface, const SwRle* rle, const RenderColor& c) static bool _rasterBlendingRle(SwSurface* surface, const SwRle* rle, const RenderRegion& bbox, const RenderColor& c)
{ {
if (surface->channelSize != sizeof(uint32_t)) return false; if (surface->channelSize != sizeof(uint32_t)) return false;
auto color = surface->join(c.r, c.g, c.b, c.a); auto color = surface->join(c.r, c.g, c.b, c.a);
const SwSpan* end;
int32_t x, len;
ARRAY_FOREACH(span, rle->spans) { for (auto span = rle->fetch(bbox, &end); span < end; ++span) {
auto dst = &surface->buf32[span->y * surface->stride + span->x]; span->fetch(bbox, x, len);
auto dst = &surface->buf32[span->y * surface->stride + x];
if (span->coverage == 255) { if (span->coverage == 255) {
for (uint32_t x = 0; x < span->len; ++x, ++dst) { for (auto x = 0; x < len; ++x, ++dst) {
*dst = surface->blender(color, *dst, 255); *dst = surface->blender(color, *dst, 255);
} }
} else { } else {
for (uint32_t x = 0; x < span->len; ++x, ++dst) { for (auto x = 0; x < len; ++x, ++dst) {
auto tmp = surface->blender(color, *dst, 255); auto tmp = surface->blender(color, *dst, 255);
*dst = INTERPOLATE(tmp, *dst, span->coverage); *dst = INTERPOLATE(tmp, *dst, span->coverage);
} }
@ -587,44 +601,47 @@ static bool _rasterBlendingRle(SwSurface* surface, const SwRle* rle, const Rende
} }
static bool _rasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderColor& c) static bool _rasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderRegion& bbox, const RenderColor& c)
{ {
#if defined(THORVG_AVX_VECTOR_SUPPORT) #if defined(THORVG_AVX_VECTOR_SUPPORT)
return avxRasterTranslucentRle(surface, rle, c); return avxRasterTranslucentRle(surface, rle, bbox, c);
#elif defined(THORVG_NEON_VECTOR_SUPPORT) #elif defined(THORVG_NEON_VECTOR_SUPPORT)
return neonRasterTranslucentRle(surface, rle, c); return neonRasterTranslucentRle(surface, rle, bbox, c);
#else #else
return cRasterTranslucentRle(surface, rle, c); return cRasterTranslucentRle(surface, rle, bbox, c);
#endif #endif
} }
static bool _rasterSolidRle(SwSurface* surface, const SwRle* rle, const RenderColor& c) static bool _rasterSolidRle(SwSurface* surface, const SwRle* rle, const RenderRegion& bbox, const RenderColor& c)
{ {
const SwSpan* end;
int32_t x, len;
//32bit channels //32bit channels
if (surface->channelSize == sizeof(uint32_t)) { if (surface->channelSize == sizeof(uint32_t)) {
auto color = surface->join(c.r, c.g, c.b, 255); auto color = surface->join(c.r, c.g, c.b, 255);
ARRAY_FOREACH(span, rle->spans) { for (auto span = rle->fetch(bbox, &end); span < end; ++span) {
if (span->coverage == 255) { span->fetch(bbox, x, len);
rasterPixel32(surface->buf32 + span->y * surface->stride, color, span->x, span->len); if (span->coverage == 255) rasterPixel32(surface->buf32 + span->y * surface->stride, color, x, len);
} else { else {
auto dst = &surface->buf32[span->y * surface->stride + span->x]; auto dst = &surface->buf32[span->y * surface->stride + x];
auto src = ALPHA_BLEND(color, span->coverage); auto src = ALPHA_BLEND(color, span->coverage);
auto ialpha = 255 - span->coverage; auto ialpha = 255 - span->coverage;
for (uint32_t x = 0; x < span->len; ++x, ++dst) { for (auto x = 0; x < len; ++x, ++dst) {
*dst = src + ALPHA_BLEND(*dst, ialpha); *dst = src + ALPHA_BLEND(*dst, ialpha);
} }
} }
} }
//8bit grayscale //8bit grayscale
} else if (surface->channelSize == sizeof(uint8_t)) { } else if (surface->channelSize == sizeof(uint8_t)) {
ARRAY_FOREACH(span, rle->spans) { for (auto span = rle->fetch(bbox, &end); span < end; ++span) {
if (span->coverage == 255) { span->fetch(bbox, x, len);
rasterGrayscale8(surface->buf8, span->coverage, span->y * surface->stride + span->x, span->len); if (span->coverage == 255) rasterGrayscale8(surface->buf8, span->coverage, span->y * surface->stride + x, len);
} else { else {
auto dst = &surface->buf8[span->y * surface->stride + span->x]; auto dst = &surface->buf8[span->y * surface->stride + x];
auto ialpha = 255 - span->coverage; auto ialpha = 255 - span->coverage;
for (uint32_t x = 0; x < span->len; ++x, ++dst) { for (auto x = 0; x < len; ++x, ++dst) {
*dst = span->coverage + MULTIPLY(*dst, ialpha); *dst = span->coverage + MULTIPLY(*dst, ialpha);
} }
} }
@ -634,18 +651,18 @@ static bool _rasterSolidRle(SwSurface* surface, const SwRle* rle, const RenderCo
} }
static bool _rasterRle(SwSurface* surface, SwRle* rle, const RenderColor& c) static bool _rasterRle(SwSurface* surface, SwRle* rle, const RenderRegion& bbox, const RenderColor& c)
{ {
if (!rle || rle->invalid()) return false; if (!rle || rle->invalid()) return false;
if (_compositing(surface)) { if (_compositing(surface)) {
if (_matting(surface)) return _rasterMattedRle(surface, rle, c); if (_matting(surface)) return _rasterMattedRle(surface, rle, bbox, c);
else return _rasterMaskedRle(surface, rle, c); else return _rasterMaskedRle(surface, rle, bbox, c);
} else if (_blending(surface)) { } else if (_blending(surface)) {
return _rasterBlendingRle(surface, rle, c); return _rasterBlendingRle(surface, rle, bbox, c);
} else { } else {
if (c.a == 255) return _rasterSolidRle(surface, rle, c); if (c.a == 255) return _rasterSolidRle(surface, rle, bbox, c);
else return _rasterTranslucentRle(surface, rle, c); else return _rasterTranslucentRle(surface, rle, bbox, c);
} }
return false; return false;
} }
@ -781,26 +798,29 @@ static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matr
/* RLE Direct Image */ /* RLE Direct Image */
/************************************************************************/ /************************************************************************/
static bool _rasterDirectMattedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) static bool _rasterDirectMattedRleImage(SwSurface* surface, const SwImage* image, const RenderRegion& bbox, uint8_t opacity)
{ {
TVGLOG("SW_ENGINE", "Direct Matted(%d) Rle Image", (int)surface->compositor->method); TVGLOG("SW_ENGINE", "Direct Matted(%d) Rle Image", (int)surface->compositor->method);
auto csize = surface->compositor->image.channelSize; auto csize = surface->compositor->image.channelSize;
auto cbuffer = surface->compositor->image.buf8; auto cbuffer = surface->compositor->image.buf8;
auto alpha = surface->alpha(surface->compositor->method); auto alpha = surface->alpha(surface->compositor->method);
const SwSpan* end;
int32_t x, len;
ARRAY_FOREACH(span, image->rle->spans) { for (auto span = image->rle->fetch(bbox, &end); span < end; ++span) {
auto dst = &surface->buf32[span->y * surface->stride + span->x]; span->fetch(bbox, x, len);
auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize]; auto dst = &surface->buf32[span->y * surface->stride + x];
auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox); auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + x) * csize];
auto img = image->buf32 + (span->y + image->oy) * image->stride + (x + image->ox);
auto a = MULTIPLY(span->coverage, opacity); auto a = MULTIPLY(span->coverage, opacity);
if (a == 255) { if (a == 255) {
for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img, cmp += csize) { for (auto x = 0; x < len; ++x, ++dst, ++img, cmp += csize) {
auto tmp = ALPHA_BLEND(*img, alpha(cmp)); auto tmp = ALPHA_BLEND(*img, alpha(cmp));
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
} }
} else { } else {
for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img, cmp += csize) { for (auto x = 0; x < len; ++x, ++dst, ++img, cmp += csize) {
auto tmp = ALPHA_BLEND(*img, MULTIPLY(a, alpha(cmp))); auto tmp = ALPHA_BLEND(*img, MULTIPLY(a, alpha(cmp)));
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
} }
@ -810,18 +830,22 @@ static bool _rasterDirectMattedRleImage(SwSurface* surface, const SwImage* image
} }
static bool _rasterDirectBlendingRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) static bool _rasterDirectBlendingRleImage(SwSurface* surface, const SwImage* image, const RenderRegion& bbox, uint8_t opacity)
{ {
ARRAY_FOREACH(span, image->rle->spans) { const SwSpan* end;
auto dst = &surface->buf32[span->y * surface->stride + span->x]; int32_t x, len;
auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox);
for (auto span = image->rle->fetch(bbox, &end); span < end; ++span) {
span->fetch(bbox, x, len);
auto dst = &surface->buf32[span->y * surface->stride + x];
auto img = image->buf32 + (span->y + image->oy) * image->stride + (x + image->ox);
auto alpha = MULTIPLY(span->coverage, opacity); auto alpha = MULTIPLY(span->coverage, opacity);
if (alpha == 255) { if (alpha == 255) {
for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { for (auto x = 0; x < len; ++x, ++dst, ++img) {
*dst = surface->blender(*img, *dst, 255); *dst = surface->blender(*img, *dst, 255);
} }
} else { } else {
for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { for (auto x = 0; x < len; ++x, ++dst, ++img) {
auto tmp = surface->blender(*img, *dst, 255); auto tmp = surface->blender(*img, *dst, 255);
*dst = INTERPOLATE(tmp, *dst, MULTIPLY(alpha, A(*img))); *dst = INTERPOLATE(tmp, *dst, MULTIPLY(alpha, A(*img)));
} }
@ -831,26 +855,30 @@ static bool _rasterDirectBlendingRleImage(SwSurface* surface, const SwImage* ima
} }
static bool _rasterDirectRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) static bool _rasterDirectRleImage(SwSurface* surface, const SwImage* image, const RenderRegion& bbox, uint8_t opacity)
{ {
ARRAY_FOREACH(span, image->rle->spans) { const SwSpan* end;
auto dst = &surface->buf32[span->y * surface->stride + span->x]; int32_t x, len;
auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox);
for (auto span = image->rle->fetch(bbox, &end); span < end; ++span) {
span->fetch(bbox, x, len);
auto dst = &surface->buf32[span->y * surface->stride + x];
auto img = image->buf32 + (span->y + image->oy) * image->stride + (x + image->ox);
auto alpha = MULTIPLY(span->coverage, opacity); auto alpha = MULTIPLY(span->coverage, opacity);
rasterTranslucentPixel32(dst, img, span->len, alpha); rasterTranslucentPixel32(dst, img, len, alpha);
} }
return true; return true;
} }
static bool _rasterDirectMaskedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) static bool _rasterDirectMaskedRleImage(SwSurface* surface, const SwImage* image, const RenderRegion& bbox, uint8_t opacity)
{ {
TVGERR("SW_ENGINE", "Not Supported Direct Masked(%d) Rle Image", (int)surface->compositor->method); TVGERR("SW_ENGINE", "Not Supported Direct Masked(%d) Rle Image", (int)surface->compositor->method);
return false; return false;
} }
static bool _directRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) static bool _directRleImage(SwSurface* surface, const SwImage* image, const RenderRegion& bbox, uint8_t opacity)
{ {
if (surface->channelSize == sizeof(uint8_t)) { if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale rle image!"); TVGERR("SW_ENGINE", "Not supported grayscale rle image!");
@ -858,12 +886,12 @@ static bool _directRleImage(SwSurface* surface, const SwImage* image, uint8_t op
} }
if (_compositing(surface)) { if (_compositing(surface)) {
if (_matting(surface)) return _rasterDirectMattedRleImage(surface, image, opacity); if (_matting(surface)) return _rasterDirectMattedRleImage(surface, image, bbox, opacity);
else return _rasterDirectMaskedRleImage(surface, image, opacity); else return _rasterDirectMaskedRleImage(surface, image, bbox, opacity);
} else if (_blending(surface)) { } else if (_blending(surface)) {
return _rasterDirectBlendingRleImage(surface, image, opacity); return _rasterDirectBlendingRleImage(surface, image, bbox, opacity);
} else { } else {
return _rasterDirectRleImage(surface, image, opacity); return _rasterDirectRleImage(surface, image, bbox, opacity);
} }
return false; return false;
} }
@ -1185,14 +1213,14 @@ static bool _rasterImage(SwSurface* surface, SwImage* image, const Matrix& trans
{ {
//RLE Image //RLE Image
if (image->rle) { if (image->rle) {
if (image->direct) return _directRleImage(surface, image, opacity); if (image->direct) return _directRleImage(surface, image, bbox, opacity);
else if (image->scaled) return _scaledRleImage(surface, image, transform, bbox, opacity); else if (image->scaled) return _scaledRleImage(surface, image, transform, bbox, opacity);
else return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity); else return _rasterTexmapPolygon(surface, image, transform, bbox, opacity);
//Whole Image //Whole Image
} else { } else {
if (image->direct) return _directImage(surface, image, bbox, opacity); if (image->direct) return _directImage(surface, image, bbox, opacity);
else if (image->scaled) return _scaledImage(surface, image, transform, bbox, opacity); else if (image->scaled) return _scaledImage(surface, image, transform, bbox, opacity);
else return _rasterTexmapPolygon(surface, image, transform, &bbox, opacity); else return _rasterTexmapPolygon(surface, image, transform, bbox, opacity);
} }
} }
@ -1666,36 +1694,35 @@ void rasterPremultiply(RenderSurface* surface)
} }
bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity) bool rasterGradientShape(SwSurface* surface, SwShape* shape, const RenderRegion& bbox, const Fill* fdata, uint8_t opacity)
{ {
if (!shape->fill) return false; if (!shape->fill) return false;
if (auto color = fillFetchSolid(shape->fill, fdata)) { if (auto color = fillFetchSolid(shape->fill, fdata)) {
auto a = MULTIPLY(color->a, opacity); auto a = MULTIPLY(color->a, opacity);
RenderColor c = {color->r, color->g, color->b, a}; RenderColor c = {color->r, color->g, color->b, a};
return a > 0 ? rasterShape(surface, shape, c) : true; return a > 0 ? rasterShape(surface, shape, bbox, c) : true;
} }
auto type = fdata->type(); auto type = fdata->type();
if (shape->fastTrack) { if (shape->fastTrack) {
if (type == Type::LinearGradient) return _rasterLinearGradientRect(surface, shape->bbox, shape->fill); if (type == Type::LinearGradient) return _rasterLinearGradientRect(surface, bbox, shape->fill);
else if (type == Type::RadialGradient)return _rasterRadialGradientRect(surface, shape->bbox, shape->fill); else if (type == Type::RadialGradient)return _rasterRadialGradientRect(surface, bbox, shape->fill);
} else if (shape->rle && shape->rle->valid()) { } else if (shape->rle && shape->rle->valid()) {
if (type == Type::LinearGradient) return _rasterLinearGradientRle(surface, shape->rle, shape->fill); if (type == Type::LinearGradient) return _rasterLinearGradientRle(surface, shape->rle, shape->fill);
else if (type == Type::RadialGradient) return _rasterRadialGradientRle(surface, shape->rle, shape->fill); else if (type == Type::RadialGradient) return _rasterRadialGradientRle(surface, shape->rle, shape->fill);
} } return false;
return false;
} }
bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity) bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const RenderRegion& bbox, const Fill* fdata, uint8_t opacity)
{ {
if (!shape->stroke || !shape->stroke->fill || !shape->strokeRle || shape->strokeRle->invalid()) return false; if (!shape->stroke || !shape->stroke->fill || !shape->strokeRle || shape->strokeRle->invalid()) return false;
if (auto color = fillFetchSolid(shape->stroke->fill, fdata)) { if (auto color = fillFetchSolid(shape->stroke->fill, fdata)) {
RenderColor c = {color->r, color->g, color->b, color->a}; RenderColor c = {color->r, color->g, color->b, color->a};
c.a = MULTIPLY(c.a, opacity); c.a = MULTIPLY(c.a, opacity);
return c.a > 0 ? rasterStroke(surface, shape, c) : true; return c.a > 0 ? rasterStroke(surface, shape, bbox, c) : true;
} }
auto type = fdata->type(); auto type = fdata->type();
@ -1705,19 +1732,19 @@ bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata,
} }
bool rasterShape(SwSurface* surface, SwShape* shape, RenderColor& c) bool rasterShape(SwSurface* surface, SwShape* shape, const RenderRegion& bbox, RenderColor& c)
{ {
if (c.a < 255) { if (c.a < 255) {
c.r = MULTIPLY(c.r, c.a); c.r = MULTIPLY(c.r, c.a);
c.g = MULTIPLY(c.g, c.a); c.g = MULTIPLY(c.g, c.a);
c.b = MULTIPLY(c.b, c.a); c.b = MULTIPLY(c.b, c.a);
} }
if (shape->fastTrack) return _rasterRect(surface, shape->bbox, c); if (shape->fastTrack) return _rasterRect(surface, bbox, c);
else return _rasterRle(surface, shape->rle, c); else return _rasterRle(surface, shape->rle, bbox, c);
} }
bool rasterStroke(SwSurface* surface, SwShape* shape, RenderColor& c) bool rasterStroke(SwSurface* surface, SwShape* shape, const RenderRegion& bbox, RenderColor& c)
{ {
if (c.a < 255) { if (c.a < 255) {
c.r = MULTIPLY(c.r, c.a); c.r = MULTIPLY(c.r, c.a);
@ -1725,7 +1752,7 @@ bool rasterStroke(SwSurface* surface, SwShape* shape, RenderColor& c)
c.b = MULTIPLY(c.b, c.a); c.b = MULTIPLY(c.b, c.a);
} }
return _rasterRle(surface, shape->strokeRle, c); return _rasterRle(surface, shape->strokeRle, bbox, c);
} }
@ -1791,4 +1818,4 @@ void rasterXYFlip(uint32_t* src, uint32_t* dst, int32_t stride, int32_t w, int32
} }
} }
} }
} }

View file

@ -158,47 +158,51 @@ static bool avxRasterTranslucentRect(SwSurface* surface, const RenderRegion& bbo
} }
static bool avxRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderColor& c) static bool avxRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderRegion& bbox, const RenderColor& c)
{ {
const SwSpan* end;
int32_t x, len;
//32bit channels //32bit channels
if (surface->channelSize == sizeof(uint32_t)) { if (surface->channelSize == sizeof(uint32_t)) {
auto color = surface->join(c.r, c.g, c.b, c.a); auto color = surface->join(c.r, c.g, c.b, c.a);
uint32_t src; uint32_t src;
ARRAY_FOREACH(span, rle->spans) { for (auto span = rle->fetch(bbox, &end); span < end; ++span) {
auto dst = &surface->buf32[span->y * surface->stride + span->x]; FETCH_BOUND(span, bbox);
span->fetch(bbox, x, len);
if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage); if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
else src = color; else src = color;
auto dst = &surface->buf32[span->y * surface->stride + x];
auto ialpha = IA(src); auto ialpha = IA(src);
//1. fill the not aligned memory (for 128-bit registers a 16-bytes alignment is required) //1. fill the not aligned memory (for 128-bit registers a 16-bytes alignment is required)
auto notAligned = ((uintptr_t)dst & 0xf) / 4; int32_t notAligned = ((uintptr_t)dst & 0xf) / 4;
if (notAligned) { if (notAligned) {
notAligned = (N_32BITS_IN_128REG - notAligned > span->len ? span->len : N_32BITS_IN_128REG - notAligned); notAligned = (N_32BITS_IN_128REG - notAligned > len ? len : N_32BITS_IN_128REG - notAligned);
for (uint32_t x = 0; x < notAligned; ++x, ++dst) { for (auto x = 0; x < notAligned; ++x, ++dst) {
*dst = src + ALPHA_BLEND(*dst, ialpha); *dst = src + ALPHA_BLEND(*dst, ialpha);
} }
} }
//2. fill the aligned memory using avx - N_32BITS_IN_128REG pixels processed at once //2. fill the aligned memory using avx - N_32BITS_IN_128REG pixels processed at once
//In order to avoid unnecessary avx variables declarations a check is made whether there are any iterations at all //In order to avoid unnecessary avx variables declarations a check is made whether there are any iterations at all
uint32_t iterations = (span->len - notAligned) / N_32BITS_IN_128REG; int32_t iterations = (len - notAligned) / N_32BITS_IN_128REG;
uint32_t avxFilled = 0; int32_t avxFilled = 0;
if (iterations > 0) { if (iterations > 0) {
auto avxSrc = _mm_set1_epi32(src); auto avxSrc = _mm_set1_epi32(src);
auto avxIalpha = _mm_set1_epi8(ialpha); auto avxIalpha = _mm_set1_epi8(ialpha);
avxFilled = iterations * N_32BITS_IN_128REG; avxFilled = iterations * N_32BITS_IN_128REG;
auto avxDst = (__m128i*)dst; auto avxDst = (__m128i*)dst;
for (uint32_t x = 0; x < iterations; ++x, ++avxDst) { for (auto x = 0; x < iterations; ++x, ++avxDst) {
*avxDst = _mm_add_epi32(avxSrc, ALPHA_BLEND(*avxDst, avxIalpha)); *avxDst = _mm_add_epi32(avxSrc, ALPHA_BLEND(*avxDst, avxIalpha));
} }
} }
//3. fill the remaining pixels //3. fill the remaining pixels
int32_t leftovers = span->len - notAligned - avxFilled; auto leftovers = len - notAligned - avxFilled;
dst += avxFilled; dst += avxFilled;
while (leftovers--) { while (leftovers--) {
*dst = src + ALPHA_BLEND(*dst, ialpha); *dst = src + ALPHA_BLEND(*dst, ialpha);
@ -211,12 +215,14 @@ static bool avxRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const
} else if (surface->channelSize == sizeof(uint8_t)) { } else if (surface->channelSize == sizeof(uint8_t)) {
TVGLOG("SW_ENGINE", "Require AVX Optimization, Channel Size = %d", surface->channelSize); TVGLOG("SW_ENGINE", "Require AVX Optimization, Channel Size = %d", surface->channelSize);
uint8_t src; uint8_t src;
ARRAY_FOREACH(span, rle->spans) { for (auto span = rle->fetch(bbox, &end); span < end; ++span) {
auto dst = &surface->buf8[span->y * surface->stride + span->x]; FETCH_BOUND(span, bbox);
span->fetch(bbox, x, len);
auto dst = &surface->buf8[span->y * surface->stride + x];
if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a); if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a);
else src = c.a; else src = c.a;
auto ialpha = ~c.a; auto ialpha = ~c.a;
for (uint32_t x = 0; x < span->len; ++x, ++dst) { for (auto x = 0; x < len; ++x, ++dst) {
*dst = src + MULTIPLY(*dst, ialpha); *dst = src + MULTIPLY(*dst, ialpha);
} }
} }

View file

@ -92,30 +92,35 @@ static void inline cRasterPixels(PIXEL_T* dst, PIXEL_T val, uint32_t offset, int
} }
static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderColor& c) static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderRegion& bbox, const RenderColor& c)
{ {
const SwSpan* end;
int32_t x, len;
//32bit channels //32bit channels
if (surface->channelSize == sizeof(uint32_t)) { if (surface->channelSize == sizeof(uint32_t)) {
auto color = surface->join(c.r, c.g, c.b, c.a); auto color = surface->join(c.r, c.g, c.b, c.a);
uint32_t src; uint32_t src;
ARRAY_FOREACH(span, rle->spans) { for (auto span = rle->fetch(bbox, &end); span < end; ++span) {
auto dst = &surface->buf32[span->y * surface->stride + span->x]; span->fetch(bbox, x, len);
auto dst = &surface->buf32[span->y * surface->stride + x];
if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage); if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
else src = color; else src = color;
auto ialpha = IA(src); auto ialpha = IA(src);
for (uint32_t x = 0; x < span->len; ++x, ++dst) { for (auto x = 0; x < len; ++x, ++dst) {
*dst = src + ALPHA_BLEND(*dst, ialpha); *dst = src + ALPHA_BLEND(*dst, ialpha);
} }
} }
//8bit grayscale //8bit grayscale
} else if (surface->channelSize == sizeof(uint8_t)) { } else if (surface->channelSize == sizeof(uint8_t)) {
uint8_t src; uint8_t src;
ARRAY_FOREACH(span, rle->spans) { for (auto span = rle->fetch(bbox, &end); span < end; ++span) {
auto dst = &surface->buf8[span->y * surface->stride + span->x]; span->fetch(bbox, x, len);
auto dst = &surface->buf8[span->y * surface->stride + x];
if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a); if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a);
else src = c.a; else src = c.a;
auto ialpha = ~c.a; auto ialpha = ~c.a;
for (uint32_t x = 0; x < span->len; ++x, ++dst) { for (auto x = 0; x < len; ++x, ++dst) {
*dst = src + MULTIPLY(*dst, ialpha); *dst = src + MULTIPLY(*dst, ialpha);
} }
} }

View file

@ -89,20 +89,25 @@ static void neonRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int3
} }
static bool neonRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderColor& c) static bool neonRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderRegion& bbox, const RenderColor& c)
{ {
const SwSpan* end;
int32_t x, len;
//32bit channels //32bit channels
if (surface->channelSize == sizeof(uint32_t)) { if (surface->channelSize == sizeof(uint32_t)) {
auto color = surface->join(c.r, c.g, c.b, c.a); auto color = surface->join(c.r, c.g, c.b, c.a);
uint32_t src; uint32_t src;
uint8x8_t *vDst = nullptr; uint8x8_t *vDst = nullptr;
uint16_t align; int32_t align;
ARRAY_FOREACH(span, rle->spans) { for (auto span = rle->fetch(bbox, &end); span < end; ++span) {
FETCH_BOUND(span, bbox);
span->fetch(bbox, x, len);
if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage); if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
else src = color; else src = color;
auto dst = &surface->buf32[span->y * surface->stride + span->x]; auto dst = &surface->buf32[span->y * surface->stride + x];
auto ialpha = IA(src); auto ialpha = IA(src);
if ((((uintptr_t) dst) & 0x7) != 0) { if ((((uintptr_t) dst) & 0x7) != 0) {
@ -118,11 +123,11 @@ static bool neonRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const
uint8x8_t vSrc = (uint8x8_t) vdup_n_u32(src); uint8x8_t vSrc = (uint8x8_t) vdup_n_u32(src);
uint8x8_t vIalpha = vdup_n_u8((uint8_t) ialpha); uint8x8_t vIalpha = vdup_n_u8((uint8_t) ialpha);
for (uint32_t x = 0; x < (span->len - align) / 2; ++x) for (int32_t x = 0; x < (len - align) / 2; ++x)
vDst[x] = vadd_u8(vSrc, ALPHA_BLEND(vDst[x], vIalpha)); vDst[x] = vadd_u8(vSrc, ALPHA_BLEND(vDst[x], vIalpha));
auto leftovers = (span->len - align) % 2; auto leftovers = (len - align) % 2;
if (leftovers > 0) dst[span->len - 1] = src + ALPHA_BLEND(dst[span->len - 1], ialpha); if (leftovers > 0) dst[len - 1] = src + ALPHA_BLEND(dst[len - 1], ialpha);
++span; ++span;
} }
@ -130,12 +135,14 @@ static bool neonRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const
} else if (surface->channelSize == sizeof(uint8_t)) { } else if (surface->channelSize == sizeof(uint8_t)) {
TVGLOG("SW_ENGINE", "Require Neon Optimization, Channel Size = %d", surface->channelSize); TVGLOG("SW_ENGINE", "Require Neon Optimization, Channel Size = %d", surface->channelSize);
uint8_t src; uint8_t src;
ARRAY_FOREACH(span, rle->spans) { for (auto span = rle->fetch(bbox, &end); span < end; ++span) {
auto dst = &surface->buf8[span->y * surface->stride + span->x]; FETCH_BOUND(span, bbox);
span->fetch(bbox, x, len);
auto dst = &surface->buf8[span->y * surface->stride + x];
if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a); if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a);
else src = c.a; else src = c.a;
auto ialpha = ~c.a; auto ialpha = ~c.a;
for (uint32_t x = 0; x < span->len; ++x, ++dst) { for (auto x = 0; x < len; ++x, ++dst) {
*dst = src + MULTIPLY(*dst, ialpha); *dst = src + MULTIPLY(*dst, ialpha);
} }
} }

View file

@ -52,33 +52,25 @@ static float xa, xb, ua, va;
//Y Range exception handling //Y Range exception handling
static bool _arrange(const SwImage* image, const RenderRegion* bbox, int& yStart, int& yEnd) static bool _arrange(const SwImage* image, const RenderRegion& bbox, int& yStart, int& yEnd)
{ {
int32_t bboxTop, bboxBottom; auto top = image->rle ? std::max((int)image->rle->spans.first().y, bbox.min.y) : (int)bbox.min.y;
auto bottom = image->rle? std::min((int)image->rle->spans.last().y, bbox.max.y) : (int)bbox.max.y;
if (bbox) { if (yStart < top) yStart = top;
bboxTop = bbox->min.y; if (yEnd > bottom) yEnd = bottom;
bboxBottom = bbox->max.y;
} else {
bboxTop = image->rle->spans.first().y;
bboxBottom = image->rle->spans.last().y;
}
if (yStart < bboxTop) yStart = bboxTop;
if (yEnd > bboxBottom) yEnd = bboxBottom;
return yEnd > yStart; return yEnd > yStart;
} }
static bool _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* image, const RenderRegion* bbox, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0) static bool _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* image, const RenderRegion& bbox, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0)
{ {
TVGERR("SW_ENGINE", "TODO: _rasterMaskedPolygonImageSegment()"); TVGERR("SW_ENGINE", "TODO: _rasterMaskedPolygonImageSegment()");
return false; return false;
} }
static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage* image, const RenderRegion* bbox, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity) static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage* image, const RenderRegion& bbox, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity)
{ {
float _dudx = dudx, _dvdx = dvdx; float _dudx = dudx, _dvdx = dvdx;
float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya; float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
@ -89,26 +81,15 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
int32_t sh = static_cast<int32_t>(image->h); int32_t sh = static_cast<int32_t>(image->h);
int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay; int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
int32_t vv = 0, uu = 0; int32_t vv = 0, uu = 0;
int32_t minx = INT32_MAX, maxx = 0; auto minx = bbox.min.x;
auto maxx = bbox.max.x;
float dx, u, v, iptr; float dx, u, v, iptr;
uint32_t* buf; uint32_t* buf;
SwSpan* span = nullptr; //used only when rle based.
if (!_arrange(image, bbox, yStart, yEnd)) return; if (!_arrange(image, bbox, yStart, yEnd)) return;
//Loop through all lines in the segment const SwSpan* span = nullptr; //used only when rle based.
uint32_t spanIdx = 0; if (image->rle) span = image->rle->fetch(yStart, yEnd, nullptr);
if (bbox) {
minx = bbox->min.x;
maxx = bbox->max.x;
} else {
span = image->rle->data();
while (span->y < yStart) {
++span;
++spanIdx;
}
}
y = yStart; y = yStart;
@ -116,16 +97,17 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
x1 = (int32_t)_xa; x1 = (int32_t)_xa;
x2 = (int32_t)_xb; x2 = (int32_t)_xb;
if (!bbox) { if (span) {
minx = INT32_MAX; minx = INT32_MAX;
maxx = 0; maxx = 0;
//one single row, could be consisted of multiple spans. //one single row, could be consisted of multiple spans.
while (span->y == y && spanIdx < image->rle->size()) { while (span->y == y && span < image->rle->spans.end()) {
if (minx > span->x) minx = span->x; if (minx > span->x) minx = span->x;
if (maxx < span->x + span->len) maxx = span->x + span->len; if (maxx < span->x + span->len) maxx = span->x + span->len;
++span; ++span;
++spanIdx;
} }
if (minx < bbox.min.x) minx = bbox.min.x;
if (maxx > bbox.max.x) maxx = bbox.max.x;
} }
if (x1 < minx) x1 = minx; if (x1 < minx) x1 = minx;
if (x2 > maxx) x2 = maxx; if (x2 > maxx) x2 = maxx;
@ -195,8 +177,7 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
_ua += _dudya; _ua += _dudya;
_va += _dvdya; _va += _dvdya;
if (!bbox && spanIdx >= image->rle->size()) break; if (span && span >= image->rle->spans.end()) break;
++y; ++y;
} }
xa = _xa; xa = _xa;
@ -206,7 +187,7 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
} }
static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const RenderRegion* bbox, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, bool matting) static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const RenderRegion& bbox, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, bool matting)
{ {
float _dudx = dudx, _dvdx = dvdx; float _dudx = dudx, _dvdx = dvdx;
float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya; float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
@ -217,10 +198,10 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
int32_t sh = static_cast<int32_t>(image->h); int32_t sh = static_cast<int32_t>(image->h);
int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay; int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
int32_t vv = 0, uu = 0; int32_t vv = 0, uu = 0;
int32_t minx = INT32_MAX, maxx = 0; auto minx = bbox.min.x;
auto maxx = bbox.max.x;
float dx, u, v, iptr; float dx, u, v, iptr;
uint32_t* buf; uint32_t* buf;
SwSpan* span = nullptr; //used only when rle based.
//for matting(composition) //for matting(composition)
auto csize = matting ? surface->compositor->image.channelSize: 0; auto csize = matting ? surface->compositor->image.channelSize: 0;
@ -229,19 +210,8 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
if (!_arrange(image, bbox, yStart, yEnd)) return; if (!_arrange(image, bbox, yStart, yEnd)) return;
//Loop through all lines in the segment const SwSpan* span = nullptr; //used only when rle based.
uint32_t spanIdx = 0; if (image->rle) span = image->rle->fetch(yStart, yEnd, nullptr);
if (bbox) {
minx = bbox->min.x;
maxx = bbox->max.x;
} else {
span = image->rle->data();
while (span->y < yStart) {
++span;
++spanIdx;
}
}
y = yStart; y = yStart;
@ -249,16 +219,17 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
x1 = (int32_t)_xa; x1 = (int32_t)_xa;
x2 = (int32_t)_xb; x2 = (int32_t)_xb;
if (!bbox) { if (span) {
minx = INT32_MAX; minx = INT32_MAX;
maxx = 0; maxx = 0;
//one single row, could be consisted of multiple spans. //one single row, could be consisted of multiple spans.
while (span->y == y && spanIdx < image->rle->size()) { while (span->y == y && span < image->rle->spans.end()) {
if (minx > span->x) minx = span->x; if (minx > span->x) minx = span->x;
if (maxx < span->x + span->len) maxx = span->x + span->len; if (maxx < span->x + span->len) maxx = span->x + span->len;
++span; ++span;
++spanIdx;
} }
if (minx < bbox.min.x) minx = bbox.min.x;
if (maxx > bbox.max.x) maxx = bbox.max.x;
} }
if (x1 < minx) x1 = minx; if (x1 < minx) x1 = minx;
if (x2 > maxx) x2 = maxx; if (x2 > maxx) x2 = maxx;
@ -387,8 +358,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
_ua += _dudya; _ua += _dudya;
_va += _dvdya; _va += _dvdya;
if (!bbox && spanIdx >= image->rle->size()) break; if (span && span >= image->rle->spans.end()) break;
++y; ++y;
} }
xa = _xa; xa = _xa;
@ -399,7 +369,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
/* This mapping algorithm is based on Mikael Kalms's. */ /* This mapping algorithm is based on Mikael Kalms's. */
static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const RenderRegion* bbox, Polygon& polygon, AASpans* aaSpans, uint8_t opacity) static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const RenderRegion& bbox, Polygon& polygon, AASpans* aaSpans, uint8_t opacity)
{ {
float x[3] = {polygon.vertex[0].pt.x, polygon.vertex[1].pt.x, polygon.vertex[2].pt.x}; float x[3] = {polygon.vertex[0].pt.x, polygon.vertex[1].pt.x, polygon.vertex[2].pt.x};
float y[3] = {polygon.vertex[0].pt.y, polygon.vertex[1].pt.y, polygon.vertex[2].pt.y}; float y[3] = {polygon.vertex[0].pt.y, polygon.vertex[1].pt.y, polygon.vertex[2].pt.y};
@ -460,7 +430,7 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
if (tvg::equal(y[0], y[1])) side = x[0] > x[1]; if (tvg::equal(y[0], y[1])) side = x[0] > x[1];
if (tvg::equal(y[1], y[2])) side = x[2] > x[1]; if (tvg::equal(y[1], y[2])) side = x[2] > x[1];
auto bboxTop = bbox ? bbox->min.y : image->rle->data()->y; //Normal Image or Rle Image? auto bboxTop = image->rle ? std::max(bbox.min.y, (int)image->rle->data()->y) : bbox.min.y;
auto compositing = _compositing(surface); //Composition required auto compositing = _compositing(surface); //Composition required
auto blending = _blending(surface); //Blending required auto blending = _blending(surface); //Blending required
@ -576,7 +546,7 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
} }
static AASpans* _AASpans(float ymin, float ymax, const SwImage* image, const RenderRegion* bbox) static AASpans* _AASpans(float ymin, float ymax, const SwImage* image, const RenderRegion& bbox)
{ {
auto yStart = static_cast<int>(ymin); auto yStart = static_cast<int>(ymin);
auto yEnd = static_cast<int>(ymax); auto yEnd = static_cast<int>(ymax);
@ -863,7 +833,7 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
| / | | / |
3 -- 2 3 -- 2
*/ */
static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix& transform, const RenderRegion* bbox, uint8_t opacity) static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity)
{ {
if (surface->channelSize == sizeof(uint8_t)) { if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!"); TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!");
@ -871,7 +841,7 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
} }
//Exceptions: No dedicated drawing area? //Exceptions: No dedicated drawing area?
if ((!image->rle && !bbox) || (image->rle && image->rle->size() == 0)) return true; if ((!image->rle && bbox.invalid()) || (image->rle && image->rle->size() == 0)) return true;
/* Prepare vertices. /* Prepare vertices.
shift XY coordinates to match the sub-pixeling technique. */ shift XY coordinates to match the sub-pixeling technique. */

View file

@ -40,7 +40,7 @@ struct SwTask : Task
{ {
SwSurface* surface = nullptr; SwSurface* surface = nullptr;
SwMpool* mpool = nullptr; SwMpool* mpool = nullptr;
RenderRegion bbox; //Rendering Region RenderRegion bbox[2] = {{}, {}}; //Rendering Region 0:current, 1:prevous
Matrix transform; Matrix transform;
Array<RenderData> clips; Array<RenderData> clips;
RenderUpdateFlag flags = RenderUpdateFlag::None; RenderUpdateFlag flags = RenderUpdateFlag::None;
@ -52,7 +52,7 @@ struct SwTask : Task
{ {
//Can we skip the synchronization? //Can we skip the synchronization?
done(); done();
return bbox; return bbox[0];
} }
virtual void dispose() = 0; virtual void dispose() = 0;
@ -92,7 +92,7 @@ struct SwShapeTask : SwTask
bool clip(SwRle* target) override bool clip(SwRle* target) override
{ {
if (shape.strokeRle) return rleClip(target, shape.strokeRle); if (shape.strokeRle) return rleClip(target, shape.strokeRle);
if (shape.fastTrack) return rleClip(target, &bbox); if (shape.fastTrack) return rleClip(target, &bbox[0]);
if (shape.rle) return rleClip(target, shape.rle); if (shape.rle) return rleClip(target, shape.rle);
return false; return false;
} }
@ -101,7 +101,7 @@ struct SwShapeTask : SwTask
{ {
//Invisible //Invisible
if (opacity == 0 && !clipper) { if (opacity == 0 && !clipper) {
bbox.reset(); bbox[0].reset();
return; return;
} }
@ -115,7 +115,7 @@ struct SwShapeTask : SwTask
updateFill = (MULTIPLY(rshape->color.a, opacity) || rshape->fill); updateFill = (MULTIPLY(rshape->color.a, opacity) || rshape->fill);
if (updateShape) shapeReset(&shape); if (updateShape) shapeReset(&shape);
if (updateFill || clipper) { if (updateFill || clipper) {
if (shapePrepare(&shape, rshape, transform, bbox, renderBox, mpool, tid, clips.count > 0 ? true : false)) { if (shapePrepare(&shape, rshape, transform, bbox[0], renderBox, mpool, tid, clips.count > 0 ? true : false)) {
if (!shapeGenRle(&shape, rshape, antialiasing(strokeWidth))) goto err; if (!shapeGenRle(&shape, rshape, antialiasing(strokeWidth))) goto err;
} else { } else {
updateFill = false; updateFill = false;
@ -135,7 +135,7 @@ struct SwShapeTask : SwTask
if (updateShape || flags & RenderUpdateFlag::Stroke) { if (updateShape || flags & RenderUpdateFlag::Stroke) {
if (strokeWidth > 0.0f) { if (strokeWidth > 0.0f) {
shapeResetStroke(&shape, rshape, transform); shapeResetStroke(&shape, rshape, transform);
if (!shapeGenStrokeRle(&shape, rshape, transform, bbox, renderBox, mpool, tid)) goto err; if (!shapeGenStrokeRle(&shape, rshape, transform, bbox[0], renderBox, mpool, tid)) goto err;
if (auto fill = rshape->strokeFill()) { if (auto fill = rshape->strokeFill()) {
auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false; auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false;
if (ctable) shapeResetStrokeFill(&shape); if (ctable) shapeResetStrokeFill(&shape);
@ -157,12 +157,11 @@ struct SwShapeTask : SwTask
if (!clipShapeRle && !clipStrokeRle) goto err; if (!clipShapeRle && !clipStrokeRle) goto err;
} }
bbox = renderBox; //sync bbox[0] = renderBox; //sync
return; return;
err: err:
bbox.reset(); bbox[0].reset();
shapeReset(&shape); shapeReset(&shape);
rleReset(shape.strokeRle); rleReset(shape.strokeRle);
shapeDelOutline(&shape, mpool, tid); shapeDelOutline(&shape, mpool, tid);
@ -188,7 +187,7 @@ struct SwImageTask : SwTask
void run(unsigned tid) override void run(unsigned tid) override
{ {
auto clipBox = bbox; auto clipBox = bbox[0];
//Convert colorspace if it's not aligned. //Convert colorspace if it's not aligned.
rasterConvertCS(source, surface->cs); rasterConvertCS(source, surface->cs);
@ -205,10 +204,10 @@ struct SwImageTask : SwTask
imageReset(&image); imageReset(&image);
if (!image.data || image.w == 0 || image.h == 0) goto end; if (!image.data || image.w == 0 || image.h == 0) goto end;
if (!imagePrepare(&image, transform, clipBox, bbox, mpool, tid)) goto end; if (!imagePrepare(&image, transform, clipBox, bbox[0], mpool, tid)) goto end;
if (clips.count > 0) { if (clips.count > 0) {
if (!imageGenRle(&image, bbox, false)) goto end; if (!imageGenRle(&image, bbox[0], false)) goto end;
if (image.rle) { if (image.rle) {
//Clear current task memorypool here if the clippers would use the same memory pool //Clear current task memorypool here if the clippers would use the same memory pool
imageDelOutline(&image, mpool, tid); imageDelOutline(&image, mpool, tid);
@ -222,6 +221,7 @@ struct SwImageTask : SwTask
} }
goto end; goto end;
err: err:
bbox[0].reset();
rleReset(image.rle); rleReset(image.rle);
end: end:
imageDelOutline(&image, mpool, tid); imageDelOutline(&image, mpool, tid);
@ -234,31 +234,6 @@ struct SwImageTask : SwTask
}; };
static void _renderFill(SwShapeTask* task, SwSurface* surface)
{
if (auto fill = task->rshape->fill) {
rasterGradientShape(surface, &task->shape, fill, task->opacity);
} else {
RenderColor c;
task->rshape->fillColor(&c.r, &c.g, &c.b, &c.a);
c.a = MULTIPLY(task->opacity, c.a);
if (c.a > 0) rasterShape(surface, &task->shape, c);
}
}
static void _renderStroke(SwShapeTask* task, SwSurface* surface)
{
if (auto strokeFill = task->rshape->strokeFill()) {
rasterGradientStroke(surface, &task->shape, strokeFill, task->opacity);
} else {
RenderColor c;
if (task->rshape->strokeFill(&c.r, &c.g, &c.b, &c.a)) {
c.a = MULTIPLY(task->opacity, c.a);
if (c.a > 0) rasterStroke(surface, &task->shape, c);
}
}
}
/************************************************************************/ /************************************************************************/
/* External Class Implementation */ /* External Class Implementation */
/************************************************************************/ /************************************************************************/
@ -292,17 +267,20 @@ SwRenderer::~SwRenderer()
bool SwRenderer::clear() bool SwRenderer::clear()
{ {
if (surface) return rasterClear(surface, 0, 0, surface->w, surface->h); if (surface) {
fulldraw = true;
return rasterClear(surface, 0, 0, surface->w, surface->h);
}
return false; return false;
} }
bool SwRenderer::sync() bool SwRenderer::sync()
{ {
//clear if the rendering was not triggered.
ARRAY_FOREACH(p, tasks) { ARRAY_FOREACH(p, tasks) {
if ((*p)->disposed) { if ((*p)->disposed) delete(*p);
delete(*p); else {
} else {
(*p)->done(); (*p)->done();
(*p)->pushed = false; (*p)->pushed = false;
} }
@ -360,7 +338,28 @@ bool SwRenderer::postUpdate()
bool SwRenderer::preRender() bool SwRenderer::preRender()
{ {
return surface != nullptr; if (!surface) return false;
if (fulldraw || !dirtyRegion.prepare(tasks.count)) return true;
//TODO: optimize to remove this iteration.
//collect the old and new dirtry regions
ARRAY_FOREACH(p, tasks) {
auto task = *p;
task->done();
auto& cur = task->bbox[0];
auto& prv = task->bbox[1];
//quick generous merge if two regions are close enough.
if (abs(cur.min.y - prv.min.y) < 5 && abs(cur.max.y - prv.max.y) < 5 && abs(cur.min.x - prv.min.x) < 5 && abs(cur.max.x - prv.max.x) < 5) {
dirtyRegion.add(RenderRegion::add(task->bbox[0], task->bbox[1]));
} else {
dirtyRegion.add(task->bbox[0]);
dirtyRegion.add(task->bbox[1]);
}
}
dirtyRegion.commit();
return true;
} }
@ -385,9 +384,15 @@ bool SwRenderer::postRender()
ARRAY_FOREACH(p, tasks) { ARRAY_FOREACH(p, tasks) {
if ((*p)->disposed) delete(*p); if ((*p)->disposed) delete(*p);
else (*p)->pushed = false; else {
(*p)->bbox[1] = (*p)->bbox[0];
(*p)->pushed = false;
}
} }
tasks.clear(); tasks.clear();
dirtyRegion.clear();
fulldraw = false;
return true; return true;
} }
@ -400,7 +405,19 @@ bool SwRenderer::renderImage(RenderData data)
if (task->opacity == 0) return true; if (task->opacity == 0) return true;
return rasterImage(surface, &task->image, task->transform, task->bbox, task->opacity); //full scene or partial rendering
if (fulldraw || task->pushed || dirtyRegion.deactivated()) {
rasterImage(surface, &task->image, task->transform, task->bbox[0], task->opacity);
} else {
ARRAY_FOREACH(p, dirtyRegion.get()) {
if (task->bbox[0].min.x >= p->max.x) break; //dirtyRegion is sorted in x order
if (task->bbox[0].intersected(*p)) {
auto bbox = RenderRegion::intersect(task->bbox[0], *p);
rasterImage(surface, &task->image, task->transform, bbox, task->opacity);
}
}
}
return true;
} }
@ -413,13 +430,50 @@ bool SwRenderer::renderShape(RenderData data)
if (task->opacity == 0) return true; if (task->opacity == 0) return true;
//Main raster stage auto fill = [](SwShapeTask* task, SwSurface* surface, const RenderRegion& bbox) {
if (task->rshape->strokeFirst()) { if (auto fill = task->rshape->fill) {
_renderStroke(task, surface); rasterGradientShape(surface, &task->shape, bbox, fill, task->opacity);
_renderFill(task, surface); } else {
RenderColor c;
task->rshape->fillColor(&c.r, &c.g, &c.b, &c.a);
c.a = MULTIPLY(task->opacity, c.a);
if (c.a > 0) rasterShape(surface, &task->shape, bbox, c);
}
};
auto stroke = [](SwShapeTask* task, SwSurface* surface, const RenderRegion& bbox) {
if (auto strokeFill = task->rshape->strokeFill()) {
rasterGradientStroke(surface, &task->shape, bbox, strokeFill, task->opacity);
} else {
RenderColor c;
if (task->rshape->strokeFill(&c.r, &c.g, &c.b, &c.a)) {
c.a = MULTIPLY(task->opacity, c.a);
if (c.a > 0) rasterStroke(surface, &task->shape, bbox, c);
}
}
};
//full scene or partial rendering
if (fulldraw || task->pushed || dirtyRegion.deactivated()) {
if (task->rshape->strokeFirst()) {
stroke(task, surface, task->bbox[0]);
fill(task, surface, task->shape.bbox);
} else {
fill(task, surface, task->shape.bbox);
stroke(task, surface, task->bbox[0]);
}
} else { } else {
_renderFill(task, surface); //TODO: skip the stroke bbox if they are invalid.
_renderStroke(task, surface); ARRAY_FOREACH(p, dirtyRegion.get()) {
if (task->bbox[0].min.x >= p->max.x) break; //dirtyRegion is sorted in x order
if (task->rshape->strokeFirst()) {
if (task->bbox[0].intersected(*p)) stroke(task, surface, RenderRegion::intersect(task->bbox[0], *p));
if (task->shape.bbox.intersected(*p)) fill(task, surface, RenderRegion::intersect(task->shape.bbox, *p));
} else {
if (task->shape.bbox.intersected(*p)) fill(task, surface, RenderRegion::intersect(task->shape.bbox, *p));
if (task->bbox[0].intersected(*p)) stroke(task, surface, RenderRegion::intersect(task->bbox[0], *p));
}
}
} }
return true; return true;
@ -480,6 +534,12 @@ bool SwRenderer::blend(BlendMethod method)
} }
void SwRenderer::damage(const RenderRegion& region)
{
dirtyRegion.add(region);
}
RenderRegion SwRenderer::region(RenderData data) RenderRegion SwRenderer::region(RenderData data)
{ {
return static_cast<SwTask*>(data)->bounds(); return static_cast<SwTask*>(data)->bounds();
@ -683,6 +743,9 @@ void SwRenderer::dispose(RenderData data)
task->done(); task->done();
task->dispose(); task->dispose();
//should be updated for the region; the current paint is removed
dirtyRegion.add(task->bbox[0]);
if (task->pushed) task->disposed = true; if (task->pushed) task->disposed = true;
else delete(task); else delete(task);
} }
@ -703,7 +766,7 @@ void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Arr
task->surface = surface; task->surface = surface;
task->mpool = mpool; task->mpool = mpool;
task->bbox = RenderRegion::intersect(vport, {{0, 0}, {int32_t(surface->w), int32_t(surface->h)}}); task->bbox[0] = RenderRegion::intersect(vport, {{0, 0}, {int32_t(surface->w), int32_t(surface->h)}});
task->transform = transform; task->transform = transform;
task->clips = clips; task->clips = clips;
task->opacity = opacity; task->opacity = opacity;

View file

@ -46,6 +46,7 @@ public:
bool postRender() override; bool postRender() override;
void dispose(RenderData data) override; void dispose(RenderData data) override;
RenderRegion region(RenderData data) override; RenderRegion region(RenderData data) override;
void damage(const RenderRegion& region) override;
RenderRegion viewport() override; RenderRegion viewport() override;
bool viewport(const RenderRegion& vp) override; bool viewport(const RenderRegion& vp) override;
bool blend(BlendMethod method) override; bool blend(BlendMethod method) override;
@ -70,12 +71,14 @@ public:
static bool term(); static bool term();
private: private:
RenderDirtyRegion dirtyRegion;
SwSurface* surface = nullptr; //active surface SwSurface* surface = nullptr; //active surface
Array<SwTask*> tasks; //async task list Array<SwTask*> tasks; //async task list
Array<SwSurface*> compositors; //render targets cache list Array<SwSurface*> compositors; //render targets cache list
SwMpool* mpool; //private memory pool SwMpool* mpool; //private memory pool
RenderRegion vport; //viewport RenderRegion vport; //viewport
bool sharedMpool; //memory-pool behavior policy bool sharedMpool; //memory-pool behavior policy
bool fulldraw = true; //buffer is cleared (need to redraw full screen)
SwRenderer(); SwRenderer();
~SwRenderer(); ~SwRenderer();

View file

@ -881,10 +881,10 @@ bool rleClip(SwRle *rle, const SwRle *clip)
Array<SwSpan> out; Array<SwSpan> out;
out.reserve(std::max(rle->spans.count, clip->spans.count)); out.reserve(std::max(rle->spans.count, clip->spans.count));
auto spans = rle->data(); const SwSpan *end;
auto end = rle->spans.end(); auto spans = rle->fetch(clip->spans.first().y, clip->spans.last().y, &end);
auto cspans = clip->data(); const SwSpan *cend;
auto cend = clip->spans.end(); auto cspans = clip->fetch(rle->spans.first().y, rle->spans.last().y, &cend);
while(spans < end && cspans < cend) { while(spans < end && cspans < cend) {
//align y-coordinates. //align y-coordinates.
@ -928,9 +928,10 @@ bool rleClip(SwRle *rle, const RenderRegion* clip)
Array<SwSpan> out; Array<SwSpan> out;
out.reserve(rle->spans.count); out.reserve(rle->spans.count);
auto data = out.data; auto data = out.data;
const SwSpan* end;
uint16_t x, len; uint16_t x, len;
ARRAY_FOREACH(p, rle->spans) { for (auto p = rle->fetch(*clip, &end); p < end; ++p) {
if (p->y >= max.y) break; if (p->y >= max.y) break;
if (p->y < min.y || p->x >= max.x || (p->x + p->len) <= min.x) continue; if (p->y < min.y || p->x >= max.x || (p->x + p->len) <= min.x) continue;
if (p->x < min.x) { if (p->x < min.x) {

View file

@ -20,6 +20,7 @@
* SOFTWARE. * SOFTWARE.
*/ */
#include <algorithm>
#include "tvgMath.h" #include "tvgMath.h"
#include "tvgRender.h" #include "tvgRender.h"
@ -116,6 +117,134 @@ void RenderRegion::intersect(const RenderRegion& rhs)
if (max.y < min.y) max.y = min.y; if (max.y < min.y) max.y = min.y;
} }
void RenderDirtyRegion::subdivide(Array<RenderRegion>& targets, uint32_t idx, RenderRegion& lhs, RenderRegion& rhs)
{
RenderRegion temp[5];
int cnt = 0;
temp[cnt++] = RenderRegion::intersect(lhs, rhs);
auto max = std::min(lhs.max.x, rhs.max.x);
auto subtract = [&](RenderRegion& lhs, RenderRegion& rhs) {
//top
if (rhs.min.y < lhs.min.y) {
temp[cnt++] = {{rhs.min.x, rhs.min.y}, {rhs.max.x, lhs.min.y}};
rhs.min.y = lhs.min.y;
}
//bottom
if (rhs.max.y > lhs.max.y) {
temp[cnt++] = {{rhs.min.x, lhs.max.y}, {rhs.max.x, rhs.max.y}};
rhs.max.y = lhs.max.y;
}
//left
if (rhs.min.x < lhs.min.x) {
temp[cnt++] = {{rhs.min.x, rhs.min.y}, {lhs.min.x, rhs.max.y}};
rhs.min.x = lhs.min.x;
}
//right
if (rhs.max.x > lhs.max.x) {
temp[cnt++] = {{lhs.max.x, rhs.min.y}, {rhs.max.x, rhs.max.y}};
//rhs.max.x = lhs.max.x;
}
};
subtract(temp[0], lhs);
subtract(temp[0], rhs);
//TODO: remove this
if (targets.reserved < targets.count + cnt - 1) {
TVGERR("RENDERER", "reserved: %d, required: %d (+%d)\n", targets.reserved, targets.count + cnt - 1, cnt - 1);
abort();
}
/* Note: We considered using a list to avoid memory shifting,
but ultimately, the array outperformed the list due to better cache locality. */
//shift data
auto dst = &targets[idx + cnt];
memmove(dst, &targets[idx + 1], sizeof(RenderRegion) * (targets.count - idx - 1));
memcpy(&targets[idx], temp, sizeof(RenderRegion) * cnt);
targets.count += (cnt - 1);
//sorting by x coord again, only for the updated region
while (dst < targets.end() && dst->min.x < max) ++dst;
stable_sort(&targets[idx], dst, [](const RenderRegion& a, const RenderRegion& b) -> bool {
return a.min.x < b.min.x;
});
}
void RenderDirtyRegion::commit()
{
if (skip || disabled) return;
auto& targets = list[current];
if (targets.empty()) return;
if (targets.count > THRESHOLD) {
skip = true;
return;
}
current = !current; //swapping buffers
auto& output = list[current];
//sorting by x coord. guarantee the stable performance: O(NlogN)
stable_sort(targets.begin(), targets.end(), [](const RenderRegion& a, const RenderRegion& b) -> bool {
return a.min.x < b.min.x;
});
//Optimized using sweep-line algorithm: O(NlogN)
for (uint32_t i = 0; i < targets.count; ++i) {
auto& lhs = targets[i];
if (lhs.invalid()) continue;
auto merged = false;
for (uint32_t j = i + 1; j < targets.count; ++j) {
auto& rhs = targets[j];
if (rhs.invalid()) continue;
if (lhs.max.x < rhs.min.x) break; //line sweeping
//fully overlapped. drop lhs
if (rhs.contained(lhs)) {
merged = true;
break;
}
//fully overlapped. replace the lhs with rhs
if (lhs.contained(rhs)) {
rhs = {};
continue;
}
//just merge & expand on x axis
if (lhs.min.y == rhs.min.y && lhs.max.y == rhs.max.y) {
if (lhs.min.x <= rhs.max.x && rhs.min.x <= lhs.max.x) {
rhs.min.x = std::min(lhs.min.x, rhs.min.x);
rhs.max.x = std::max(lhs.max.x, rhs.max.x);
merged = true;
break;
}
}
//just merge & expand on y axis
if (lhs.min.x == rhs.min.x && lhs.max.x == rhs.max.x) {
if (lhs.min.y <= rhs.max.y && rhs.min.y < lhs.max.y) {
rhs.min.y = std::min(lhs.min.y, rhs.min.y);
rhs.max.y = std::max(lhs.max.y, rhs.max.y);
merged = true;
break;
}
}
//subdivide regions
if (lhs.intersected(rhs)) {
subdivide(targets, j, lhs, rhs);
merged = true;
break;
}
}
if (!merged) output.push(lhs); //this region is complete isolated
lhs = {};
}
}
/************************************************************************/ /************************************************************************/
/* RenderTrimPath Class Implementation */ /* RenderTrimPath Class Implementation */
/************************************************************************/ /************************************************************************/

View file

@ -50,7 +50,6 @@ static inline RenderUpdateFlag operator|(const RenderUpdateFlag a, const RenderU
return RenderUpdateFlag(uint16_t(a) | uint16_t(b)); return RenderUpdateFlag(uint16_t(a) | uint16_t(b));
} }
struct RenderSurface struct RenderSurface
{ {
union { union {
@ -107,6 +106,11 @@ struct RenderRegion
return {{std::max(lhs.min.x, rhs.min.x), std::max(lhs.min.y, rhs.min.y)}, {std::min(lhs.max.x, rhs.max.x), std::min(lhs.max.y, rhs.max.y)}}; return {{std::max(lhs.min.x, rhs.min.x), std::max(lhs.min.y, rhs.min.y)}, {std::min(lhs.max.x, rhs.max.x), std::min(lhs.max.y, rhs.max.y)}};
} }
static constexpr RenderRegion add(const RenderRegion& lhs, const RenderRegion& rhs)
{
return {{std::min(lhs.min.x, rhs.min.x), std::min(lhs.min.y, rhs.min.y)}, {std::max(lhs.max.x, rhs.max.x), std::max(lhs.max.y, rhs.max.y)}};
}
void intersect(const RenderRegion& rhs); void intersect(const RenderRegion& rhs);
void add(const RenderRegion& rhs) void add(const RenderRegion& rhs)
@ -117,6 +121,16 @@ struct RenderRegion
if (rhs.max.y > max.y) max.y = rhs.max.y; if (rhs.max.y > max.y) max.y = rhs.max.y;
} }
bool contained(const RenderRegion& rhs)
{
return (min.x <= rhs.min.x && max.x >= rhs.max.x && min.y <= rhs.min.y && max.y >= rhs.max.y);
}
bool intersected(const RenderRegion& rhs) const
{
return (rhs.min.x < max.x && rhs.max.x > min.x && rhs.min.y < max.y && rhs.max.y > min.y);
}
bool operator==(const RenderRegion& rhs) const bool operator==(const RenderRegion& rhs) const
{ {
return (min.x == rhs.min.x && min.y == rhs.min.y && max.x == rhs.max.x && max.y == rhs.max.y); return (min.x == rhs.min.x && min.y == rhs.min.y && max.x == rhs.max.x && max.y == rhs.max.y);
@ -137,6 +151,68 @@ struct RenderRegion
uint32_t h() const { return (uint32_t) sh(); } uint32_t h() const { return (uint32_t) sh(); }
}; };
struct RenderDirtyRegion
{
void add(const RenderRegion& region)
{
if (!disabled && region.valid()) {
list[current].push(region);
}
}
bool prepare(uint32_t count = 0)
{
if (disabled) return false;
if (count > THRESHOLD) {
skip = true;
return false;
}
count *= 120; //FIXME: enough?
list[0].reserve(count);
list[1].reserve(count);
return true;
}
bool deactivated()
{
if (disabled || skip) return true;
return false;
}
void clear()
{
list[0].clear();
list[1].clear();
skip = false;
}
const Array<RenderRegion>& get()
{
return list[current];
}
void commit();
private:
void subdivide(Array<RenderRegion>& targets, uint32_t idx, RenderRegion& lhs, RenderRegion& rhs);
/* We deactivate partial rendering if there are more than N moving elements.
Imagine thousands of moving objects covering the entire screen, That case partial rendering will lose any benefits.
Even if they don't, the overhead of subdividing and merging partial regions
could be more expensive than simply rendering the full screen.
The number is experimentally confirmed and we are open to improve this. */
static constexpr const uint32_t THRESHOLD = 5000;
Array<RenderRegion> list[2]; //double buffer swapping
uint8_t current = 0; //list index. 0 or 1
bool disabled = false;
bool skip = false;
};
struct RenderPath struct RenderPath
{ {
Array<PathCommand> cmds; Array<PathCommand> cmds;
@ -416,7 +492,7 @@ struct RenderEffectTritone : RenderEffect
class RenderMethod class RenderMethod
{ {
private: private:
uint32_t refCnt = 0; //reference count uint32_t refCnt = 0;
Key key; Key key;
public: public:
@ -433,6 +509,7 @@ public:
virtual bool renderImage(RenderData data) = 0; virtual bool renderImage(RenderData data) = 0;
virtual bool postRender() = 0; virtual bool postRender() = 0;
virtual void dispose(RenderData data) = 0; virtual void dispose(RenderData data) = 0;
virtual void damage(const RenderRegion& region) = 0;
virtual RenderRegion region(RenderData data) = 0; virtual RenderRegion region(RenderData data) = 0;
virtual RenderRegion viewport() = 0; virtual RenderRegion viewport() = 0;
virtual bool viewport(const RenderRegion& vp) = 0; virtual bool viewport(const RenderRegion& vp) = 0;

View file

@ -105,7 +105,7 @@ struct SceneImpl : Scene
RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flag, TVG_UNUSED bool clipper) RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flag, TVG_UNUSED bool clipper)
{ {
this->vport = renderer->viewport(); vport = renderer->viewport();
if (needComposition(opacity)) { if (needComposition(opacity)) {
/* Overriding opacity value. If this scene is half-translucent, /* Overriding opacity value. If this scene is half-translucent,
@ -123,6 +123,9 @@ struct SceneImpl : Scene
} }
} }
if (compFlag) vport = bounds(renderer);
if (effects) renderer->damage(vport);
return nullptr; return nullptr;
} }
@ -134,7 +137,7 @@ struct SceneImpl : Scene
renderer->blend(impl.blendMethod); renderer->blend(impl.blendMethod);
if (compFlag) { if (compFlag) {
cmp = renderer->target(bounds(renderer), renderer->colorSpace(), static_cast<CompositionFlag>(compFlag)); cmp = renderer->target(vport, renderer->colorSpace(), static_cast<CompositionFlag>(compFlag));
renderer->beginComposite(cmp, MaskMethod::None, opacity); renderer->beginComposite(cmp, MaskMethod::None, opacity);
} }
@ -157,7 +160,7 @@ struct SceneImpl : Scene
return ret; return ret;
} }
RenderRegion bounds(RenderMethod* renderer) const RenderRegion bounds(RenderMethod* renderer)
{ {
if (paints.empty()) return {}; if (paints.empty()) return {};
@ -185,8 +188,8 @@ struct SceneImpl : Scene
pRegion.max.x += eRegion.max.x; pRegion.max.x += eRegion.max.x;
pRegion.max.y += eRegion.max.y; pRegion.max.y += eRegion.max.y;
pRegion.intersect(this->vport); vport = RenderRegion::intersect(renderer->viewport(), pRegion);
return pRegion; return vport;
} }
Result bounds(Point* pt4, Matrix& m, bool obb, bool stroking) Result bounds(Point* pt4, Matrix& m, bool obb, bool stroking)
@ -298,6 +301,7 @@ struct SceneImpl : Scene
} }
delete(effects); delete(effects);
effects = nullptr; effects = nullptr;
impl.renderer->damage(vport);
} }
return Result::Success; return Result::Success;
} }

View file

@ -274,6 +274,12 @@ void WgRenderer::dispose(RenderData data) {
} }
void WgRenderer::damage(TVG_UNUSED const RenderRegion& region)
{
//TODO:
}
RenderRegion WgRenderer::region(RenderData data) RenderRegion WgRenderer::region(RenderData data)
{ {
auto renderData = (WgRenderDataPaint*)data; auto renderData = (WgRenderDataPaint*)data;

View file

@ -38,6 +38,7 @@ public:
bool postRender() override; bool postRender() override;
void dispose(RenderData data) override; void dispose(RenderData data) override;
RenderRegion region(RenderData data) override; RenderRegion region(RenderData data) override;
void damage(const RenderRegion& region) override;
RenderRegion viewport() override; RenderRegion viewport() override;
bool viewport(const RenderRegion& vp) override; bool viewport(const RenderRegion& vp) override;
bool blend(BlendMethod method) override; bool blend(BlendMethod method) override;