mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 05:33:36 +00:00
Merge 103a557b6c
into 7a0c0e4000
This commit is contained in:
commit
0b991705ad
16 changed files with 585 additions and 253 deletions
|
@ -859,6 +859,12 @@ bool GlRenderer::sync()
|
|||
}
|
||||
|
||||
|
||||
void GlRenderer::damage(TVG_UNUSED const RenderRegion& region)
|
||||
{
|
||||
//TODO:
|
||||
}
|
||||
|
||||
|
||||
RenderRegion GlRenderer::region(RenderData data)
|
||||
{
|
||||
if (currentPass()->isEmpty()) return {};
|
||||
|
|
|
@ -80,6 +80,7 @@ public:
|
|||
bool postRender() override;
|
||||
void dispose(RenderData data) override;;
|
||||
RenderRegion region(RenderData data) override;
|
||||
void damage(const RenderRegion& region) override;
|
||||
RenderRegion viewport() override;
|
||||
bool viewport(const RenderRegion& vp) override;
|
||||
bool blend(BlendMethod method) override;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#ifndef _TVG_SW_COMMON_H_
|
||||
#define _TVG_SW_COMMON_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include "tvgCommon.h"
|
||||
#include "tvgMath.h"
|
||||
#include "tvgRender.h"
|
||||
|
@ -117,22 +118,47 @@ struct SwSpan
|
|||
uint16_t x, y;
|
||||
uint16_t len;
|
||||
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
|
||||
{
|
||||
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; }
|
||||
SwSpan* data() const { return spans.data; }
|
||||
};
|
||||
|
@ -566,11 +592,11 @@ SwOutline* mpoolReqDashOutline(SwMpool* mpool, unsigned idx);
|
|||
void mpoolRetDashOutline(SwMpool* mpool, unsigned idx);
|
||||
|
||||
bool rasterCompositor(SwSurface* surface);
|
||||
bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity);
|
||||
bool rasterShape(SwSurface* surface, SwShape* shape, RenderColor& c);
|
||||
bool rasterGradientShape(SwSurface* surface, SwShape* shape, const RenderRegion& bbox, const Fill* fdata, uint8_t opacity);
|
||||
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 rasterStroke(SwSurface* surface, SwShape* shape, RenderColor& c);
|
||||
bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity);
|
||||
bool rasterStroke(SwSurface* surface, SwShape* shape, const RenderRegion& bbox, RenderColor& c);
|
||||
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);
|
||||
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);
|
||||
|
|
|
@ -472,37 +472,44 @@ static bool _rasterRect(SwSurface* surface, const RenderRegion& bbox, const Rend
|
|||
/* 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 cstride = surface->compositor->image.stride;
|
||||
const SwSpan* end;
|
||||
int32_t x, len;
|
||||
uint8_t src;
|
||||
|
||||
ARRAY_FOREACH(span, rle->spans) {
|
||||
auto cmp = &cbuffer[span->y * cstride + span->x];
|
||||
for (auto span = rle->fetch(bbox, &end); span < end; ++span) {
|
||||
span->fetch(bbox, x, len);
|
||||
auto cmp = &cbuffer[span->y * cstride + x];
|
||||
if (span->coverage == 255) src = a;
|
||||
else src = MULTIPLY(a, span->coverage);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 cstride = surface->compositor->image.stride;
|
||||
const SwSpan* end;
|
||||
int32_t x, len;
|
||||
uint8_t src;
|
||||
|
||||
ARRAY_FOREACH(span, rle->spans) {
|
||||
auto cmp = &cbuffer[span->y * cstride + span->x];
|
||||
auto dst = &surface->buf8[span->y * surface->stride + span->x];
|
||||
for (auto span = rle->fetch(bbox, &end); span < end; ++span) {
|
||||
span->fetch(bbox, x, len);
|
||||
auto cmp = &cbuffer[span->y * cstride + x];
|
||||
auto dst = &surface->buf8[span->y * surface->stride + x];
|
||||
if (span->coverage == 255) src = a;
|
||||
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
|
||||
*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);
|
||||
|
||||
|
@ -519,30 +526,33 @@ static bool _rasterMaskedRle(SwSurface* surface, SwRle* rle, const RenderColor&
|
|||
if (surface->channelSize != sizeof(uint8_t)) return false;
|
||||
|
||||
auto maskOp = _getMaskOp(surface->compositor->method);
|
||||
if (_direct(surface->compositor->method)) return _rasterDirectMaskedRle(surface, rle, maskOp, c.a);
|
||||
else return _rasterCompositeMaskedRle(surface, rle, maskOp, c.a);
|
||||
if (_direct(surface->compositor->method)) return _rasterDirectMaskedRle(surface, rle, bbox, maskOp, c.a);
|
||||
else return _rasterCompositeMaskedRle(surface, rle, bbox, maskOp, c.a);
|
||||
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);
|
||||
|
||||
auto cbuffer = surface->compositor->image.buf8;
|
||||
auto csize = surface->compositor->image.channelSize;
|
||||
auto alpha = surface->alpha(surface->compositor->method);
|
||||
const SwSpan* end;
|
||||
int32_t x, len;
|
||||
|
||||
//32bit channels
|
||||
if (surface->channelSize == sizeof(uint32_t)) {
|
||||
uint32_t src;
|
||||
auto color = surface->join(c.r, c.g, c.b, c.a);
|
||||
ARRAY_FOREACH(span, rle->spans) {
|
||||
auto dst = &surface->buf32[span->y * surface->stride + span->x];
|
||||
auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize];
|
||||
for (auto span = rle->fetch(bbox, &end); span < end; ++span) {
|
||||
span->fetch(bbox, x, len);
|
||||
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;
|
||||
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));
|
||||
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
|
||||
}
|
||||
|
@ -550,12 +560,13 @@ static bool _rasterMattedRle(SwSurface* surface, SwRle* rle, const RenderColor&
|
|||
//8bit grayscale
|
||||
} else if (surface->channelSize == sizeof(uint8_t)) {
|
||||
uint8_t src;
|
||||
ARRAY_FOREACH(span, rle->spans) {
|
||||
auto dst = &surface->buf8[span->y * surface->stride + span->x];
|
||||
auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize];
|
||||
for (auto span = rle->fetch(bbox, &end); span < end; ++span) {
|
||||
span->fetch(bbox, x, len);
|
||||
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;
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
auto color = surface->join(c.r, c.g, c.b, c.a);
|
||||
const SwSpan* end;
|
||||
int32_t x, len;
|
||||
|
||||
ARRAY_FOREACH(span, rle->spans) {
|
||||
auto dst = &surface->buf32[span->y * surface->stride + span->x];
|
||||
for (auto span = rle->fetch(bbox, &end); span < end; ++span) {
|
||||
span->fetch(bbox, x, len);
|
||||
auto dst = &surface->buf32[span->y * surface->stride + x];
|
||||
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);
|
||||
}
|
||||
} 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);
|
||||
*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)
|
||||
return avxRasterTranslucentRle(surface, rle, c);
|
||||
return avxRasterTranslucentRle(surface, rle, bbox, c);
|
||||
#elif defined(THORVG_NEON_VECTOR_SUPPORT)
|
||||
return neonRasterTranslucentRle(surface, rle, c);
|
||||
return neonRasterTranslucentRle(surface, rle, bbox, c);
|
||||
#else
|
||||
return cRasterTranslucentRle(surface, rle, c);
|
||||
return cRasterTranslucentRle(surface, rle, bbox, c);
|
||||
#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
|
||||
if (surface->channelSize == sizeof(uint32_t)) {
|
||||
auto color = surface->join(c.r, c.g, c.b, 255);
|
||||
ARRAY_FOREACH(span, rle->spans) {
|
||||
if (span->coverage == 255) {
|
||||
rasterPixel32(surface->buf32 + span->y * surface->stride, color, span->x, span->len);
|
||||
} else {
|
||||
auto dst = &surface->buf32[span->y * surface->stride + span->x];
|
||||
for (auto span = rle->fetch(bbox, &end); span < end; ++span) {
|
||||
span->fetch(bbox, x, len);
|
||||
if (span->coverage == 255) rasterPixel32(surface->buf32 + span->y * surface->stride, color, x, len);
|
||||
else {
|
||||
auto dst = &surface->buf32[span->y * surface->stride + x];
|
||||
auto src = ALPHA_BLEND(color, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
//8bit grayscale
|
||||
} else if (surface->channelSize == sizeof(uint8_t)) {
|
||||
ARRAY_FOREACH(span, rle->spans) {
|
||||
if (span->coverage == 255) {
|
||||
rasterGrayscale8(surface->buf8, span->coverage, span->y * surface->stride + span->x, span->len);
|
||||
} else {
|
||||
auto dst = &surface->buf8[span->y * surface->stride + span->x];
|
||||
for (auto span = rle->fetch(bbox, &end); span < end; ++span) {
|
||||
span->fetch(bbox, x, len);
|
||||
if (span->coverage == 255) rasterGrayscale8(surface->buf8, span->coverage, span->y * surface->stride + x, len);
|
||||
else {
|
||||
auto dst = &surface->buf8[span->y * surface->stride + x];
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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 (_compositing(surface)) {
|
||||
if (_matting(surface)) return _rasterMattedRle(surface, rle, c);
|
||||
else return _rasterMaskedRle(surface, rle, c);
|
||||
if (_matting(surface)) return _rasterMattedRle(surface, rle, bbox, c);
|
||||
else return _rasterMaskedRle(surface, rle, bbox, c);
|
||||
} else if (_blending(surface)) {
|
||||
return _rasterBlendingRle(surface, rle, c);
|
||||
return _rasterBlendingRle(surface, rle, bbox, c);
|
||||
} else {
|
||||
if (c.a == 255) return _rasterSolidRle(surface, rle, c);
|
||||
else return _rasterTranslucentRle(surface, rle, c);
|
||||
if (c.a == 255) return _rasterSolidRle(surface, rle, bbox, c);
|
||||
else return _rasterTranslucentRle(surface, rle, bbox, c);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -781,26 +798,29 @@ static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matr
|
|||
/* 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);
|
||||
|
||||
auto csize = surface->compositor->image.channelSize;
|
||||
auto cbuffer = surface->compositor->image.buf8;
|
||||
auto alpha = surface->alpha(surface->compositor->method);
|
||||
const SwSpan* end;
|
||||
int32_t x, len;
|
||||
|
||||
ARRAY_FOREACH(span, image->rle->spans) {
|
||||
auto dst = &surface->buf32[span->y * surface->stride + span->x];
|
||||
auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize];
|
||||
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 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);
|
||||
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));
|
||||
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
|
||||
}
|
||||
} 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)));
|
||||
*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) {
|
||||
auto dst = &surface->buf32[span->y * surface->stride + span->x];
|
||||
auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox);
|
||||
const SwSpan* end;
|
||||
int32_t x, len;
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
} 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);
|
||||
*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) {
|
||||
auto dst = &surface->buf32[span->y * surface->stride + span->x];
|
||||
auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox);
|
||||
const SwSpan* end;
|
||||
int32_t x, len;
|
||||
|
||||
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);
|
||||
rasterTranslucentPixel32(dst, img, span->len, alpha);
|
||||
rasterTranslucentPixel32(dst, img, len, alpha);
|
||||
}
|
||||
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);
|
||||
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)) {
|
||||
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 (_matting(surface)) return _rasterDirectMattedRleImage(surface, image, opacity);
|
||||
else return _rasterDirectMaskedRleImage(surface, image, opacity);
|
||||
if (_matting(surface)) return _rasterDirectMattedRleImage(surface, image, bbox, opacity);
|
||||
else return _rasterDirectMaskedRleImage(surface, image, bbox, opacity);
|
||||
} else if (_blending(surface)) {
|
||||
return _rasterDirectBlendingRleImage(surface, image, opacity);
|
||||
return _rasterDirectBlendingRleImage(surface, image, bbox, opacity);
|
||||
} else {
|
||||
return _rasterDirectRleImage(surface, image, opacity);
|
||||
return _rasterDirectRleImage(surface, image, bbox, opacity);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -1185,14 +1213,14 @@ static bool _rasterImage(SwSurface* surface, SwImage* image, const Matrix& trans
|
|||
{
|
||||
//RLE Image
|
||||
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 return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity);
|
||||
else return _rasterTexmapPolygon(surface, image, transform, bbox, opacity);
|
||||
//Whole Image
|
||||
} else {
|
||||
if (image->direct) return _directImage(surface, image, 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 (auto color = fillFetchSolid(shape->fill, fdata)) {
|
||||
auto a = MULTIPLY(color->a, opacity);
|
||||
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();
|
||||
if (shape->fastTrack) {
|
||||
if (type == Type::LinearGradient) return _rasterLinearGradientRect(surface, shape->bbox, shape->fill);
|
||||
else if (type == Type::RadialGradient)return _rasterRadialGradientRect(surface, shape->bbox, shape->fill);
|
||||
if (type == Type::LinearGradient) return _rasterLinearGradientRect(surface, bbox, shape->fill);
|
||||
else if (type == Type::RadialGradient)return _rasterRadialGradientRect(surface, bbox, shape->fill);
|
||||
} else if (shape->rle && shape->rle->valid()) {
|
||||
if (type == Type::LinearGradient) return _rasterLinearGradientRle(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 (auto color = fillFetchSolid(shape->stroke->fill, fdata)) {
|
||||
RenderColor c = {color->r, color->g, color->b, color->a};
|
||||
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();
|
||||
|
@ -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) {
|
||||
c.r = MULTIPLY(c.r, c.a);
|
||||
c.g = MULTIPLY(c.g, c.a);
|
||||
c.b = MULTIPLY(c.b, c.a);
|
||||
}
|
||||
if (shape->fastTrack) return _rasterRect(surface, shape->bbox, c);
|
||||
else return _rasterRle(surface, shape->rle, c);
|
||||
if (shape->fastTrack) return _rasterRect(surface, bbox, 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) {
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
if (surface->channelSize == sizeof(uint32_t)) {
|
||||
auto color = surface->join(c.r, c.g, c.b, c.a);
|
||||
uint32_t src;
|
||||
|
||||
ARRAY_FOREACH(span, rle->spans) {
|
||||
auto dst = &surface->buf32[span->y * surface->stride + span->x];
|
||||
|
||||
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);
|
||||
else src = color;
|
||||
|
||||
auto dst = &surface->buf32[span->y * surface->stride + x];
|
||||
auto ialpha = IA(src);
|
||||
|
||||
//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) {
|
||||
notAligned = (N_32BITS_IN_128REG - notAligned > span->len ? span->len : N_32BITS_IN_128REG - notAligned);
|
||||
for (uint32_t x = 0; x < notAligned; ++x, ++dst) {
|
||||
notAligned = (N_32BITS_IN_128REG - notAligned > len ? len : N_32BITS_IN_128REG - notAligned);
|
||||
for (auto x = 0; x < notAligned; ++x, ++dst) {
|
||||
*dst = src + ALPHA_BLEND(*dst, ialpha);
|
||||
}
|
||||
}
|
||||
|
||||
//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
|
||||
uint32_t iterations = (span->len - notAligned) / N_32BITS_IN_128REG;
|
||||
uint32_t avxFilled = 0;
|
||||
int32_t iterations = (len - notAligned) / N_32BITS_IN_128REG;
|
||||
int32_t avxFilled = 0;
|
||||
if (iterations > 0) {
|
||||
auto avxSrc = _mm_set1_epi32(src);
|
||||
auto avxIalpha = _mm_set1_epi8(ialpha);
|
||||
|
||||
avxFilled = iterations * N_32BITS_IN_128REG;
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
//3. fill the remaining pixels
|
||||
int32_t leftovers = span->len - notAligned - avxFilled;
|
||||
auto leftovers = len - notAligned - avxFilled;
|
||||
dst += avxFilled;
|
||||
while (leftovers--) {
|
||||
*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)) {
|
||||
TVGLOG("SW_ENGINE", "Require AVX Optimization, Channel Size = %d", surface->channelSize);
|
||||
uint8_t src;
|
||||
ARRAY_FOREACH(span, rle->spans) {
|
||||
auto dst = &surface->buf8[span->y * surface->stride + span->x];
|
||||
for (auto span = rle->fetch(bbox, &end); span < end; ++span) {
|
||||
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);
|
||||
else src = 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
if (surface->channelSize == sizeof(uint32_t)) {
|
||||
auto color = surface->join(c.r, c.g, c.b, c.a);
|
||||
uint32_t src;
|
||||
ARRAY_FOREACH(span, rle->spans) {
|
||||
auto dst = &surface->buf32[span->y * surface->stride + span->x];
|
||||
for (auto span = rle->fetch(bbox, &end); span < end; ++span) {
|
||||
span->fetch(bbox, x, len);
|
||||
auto dst = &surface->buf32[span->y * surface->stride + x];
|
||||
if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
|
||||
else src = color;
|
||||
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);
|
||||
}
|
||||
}
|
||||
//8bit grayscale
|
||||
} else if (surface->channelSize == sizeof(uint8_t)) {
|
||||
uint8_t src;
|
||||
ARRAY_FOREACH(span, rle->spans) {
|
||||
auto dst = &surface->buf8[span->y * surface->stride + span->x];
|
||||
for (auto span = rle->fetch(bbox, &end); span < end; ++span) {
|
||||
span->fetch(bbox, x, len);
|
||||
auto dst = &surface->buf8[span->y * surface->stride + x];
|
||||
if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a);
|
||||
else src = 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
if (surface->channelSize == sizeof(uint32_t)) {
|
||||
auto color = surface->join(c.r, c.g, c.b, c.a);
|
||||
uint32_t src;
|
||||
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);
|
||||
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);
|
||||
|
||||
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 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));
|
||||
|
||||
auto leftovers = (span->len - align) % 2;
|
||||
if (leftovers > 0) dst[span->len - 1] = src + ALPHA_BLEND(dst[span->len - 1], ialpha);
|
||||
auto leftovers = (len - align) % 2;
|
||||
if (leftovers > 0) dst[len - 1] = src + ALPHA_BLEND(dst[len - 1], ialpha);
|
||||
|
||||
++span;
|
||||
}
|
||||
|
@ -130,12 +135,14 @@ static bool neonRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const
|
|||
} else if (surface->channelSize == sizeof(uint8_t)) {
|
||||
TVGLOG("SW_ENGINE", "Require Neon Optimization, Channel Size = %d", surface->channelSize);
|
||||
uint8_t src;
|
||||
ARRAY_FOREACH(span, rle->spans) {
|
||||
auto dst = &surface->buf8[span->y * surface->stride + span->x];
|
||||
for (auto span = rle->fetch(bbox, &end); span < end; ++span) {
|
||||
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);
|
||||
else src = 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,33 +52,25 @@ static float xa, xb, ua, va;
|
|||
|
||||
|
||||
//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;
|
||||
|
||||
if (bbox) {
|
||||
bboxTop = bbox->min.y;
|
||||
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;
|
||||
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 (yStart < top) yStart = top;
|
||||
if (yEnd > bottom) yEnd = bottom;
|
||||
|
||||
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()");
|
||||
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 _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 x1, x2, x, y, ar, ab, iru, irv, px, ay;
|
||||
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;
|
||||
uint32_t* buf;
|
||||
SwSpan* span = nullptr; //used only when rle based.
|
||||
|
||||
if (!_arrange(image, bbox, yStart, yEnd)) return;
|
||||
|
||||
//Loop through all lines in the segment
|
||||
uint32_t spanIdx = 0;
|
||||
|
||||
if (bbox) {
|
||||
minx = bbox->min.x;
|
||||
maxx = bbox->max.x;
|
||||
} else {
|
||||
span = image->rle->data();
|
||||
while (span->y < yStart) {
|
||||
++span;
|
||||
++spanIdx;
|
||||
}
|
||||
}
|
||||
const SwSpan* span = nullptr; //used only when rle based.
|
||||
if (image->rle) span = image->rle->fetch(yStart, yEnd, nullptr);
|
||||
|
||||
y = yStart;
|
||||
|
||||
|
@ -116,16 +97,17 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
|
|||
x1 = (int32_t)_xa;
|
||||
x2 = (int32_t)_xb;
|
||||
|
||||
if (!bbox) {
|
||||
if (span) {
|
||||
minx = INT32_MAX;
|
||||
maxx = 0;
|
||||
//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 (maxx < span->x + span->len) maxx = span->x + span->len;
|
||||
++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 (x2 > maxx) x2 = maxx;
|
||||
|
@ -195,8 +177,7 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
|
|||
_ua += _dudya;
|
||||
_va += _dvdya;
|
||||
|
||||
if (!bbox && spanIdx >= image->rle->size()) break;
|
||||
|
||||
if (span && span >= image->rle->spans.end()) break;
|
||||
++y;
|
||||
}
|
||||
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 _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 x1, x2, x, y, ar, ab, iru, irv, px, ay;
|
||||
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;
|
||||
uint32_t* buf;
|
||||
SwSpan* span = nullptr; //used only when rle based.
|
||||
|
||||
//for matting(composition)
|
||||
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;
|
||||
|
||||
//Loop through all lines in the segment
|
||||
uint32_t spanIdx = 0;
|
||||
|
||||
if (bbox) {
|
||||
minx = bbox->min.x;
|
||||
maxx = bbox->max.x;
|
||||
} else {
|
||||
span = image->rle->data();
|
||||
while (span->y < yStart) {
|
||||
++span;
|
||||
++spanIdx;
|
||||
}
|
||||
}
|
||||
const SwSpan* span = nullptr; //used only when rle based.
|
||||
if (image->rle) span = image->rle->fetch(yStart, yEnd, nullptr);
|
||||
|
||||
y = yStart;
|
||||
|
||||
|
@ -249,16 +219,17 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
|
|||
x1 = (int32_t)_xa;
|
||||
x2 = (int32_t)_xb;
|
||||
|
||||
if (!bbox) {
|
||||
if (span) {
|
||||
minx = INT32_MAX;
|
||||
maxx = 0;
|
||||
//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 (maxx < span->x + span->len) maxx = span->x + span->len;
|
||||
++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 (x2 > maxx) x2 = maxx;
|
||||
|
@ -387,8 +358,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
|
|||
_ua += _dudya;
|
||||
_va += _dvdya;
|
||||
|
||||
if (!bbox && spanIdx >= image->rle->size()) break;
|
||||
|
||||
if (span && span >= image->rle->spans.end()) break;
|
||||
++y;
|
||||
}
|
||||
xa = _xa;
|
||||
|
@ -399,7 +369,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
|
|||
|
||||
|
||||
/* 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 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[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 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 yEnd = static_cast<int>(ymax);
|
||||
|
@ -863,7 +833,7 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
|
|||
| / |
|
||||
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)) {
|
||||
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?
|
||||
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.
|
||||
shift XY coordinates to match the sub-pixeling technique. */
|
||||
|
|
|
@ -40,7 +40,7 @@ struct SwTask : Task
|
|||
{
|
||||
SwSurface* surface = nullptr;
|
||||
SwMpool* mpool = nullptr;
|
||||
RenderRegion bbox; //Rendering Region
|
||||
RenderRegion bbox[2] = {{}, {}}; //Rendering Region 0:current, 1:prevous
|
||||
Matrix transform;
|
||||
Array<RenderData> clips;
|
||||
RenderUpdateFlag flags = RenderUpdateFlag::None;
|
||||
|
@ -52,7 +52,7 @@ struct SwTask : Task
|
|||
{
|
||||
//Can we skip the synchronization?
|
||||
done();
|
||||
return bbox;
|
||||
return bbox[0];
|
||||
}
|
||||
|
||||
virtual void dispose() = 0;
|
||||
|
@ -92,7 +92,7 @@ struct SwShapeTask : SwTask
|
|||
bool clip(SwRle* target) override
|
||||
{
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ struct SwShapeTask : SwTask
|
|||
{
|
||||
//Invisible
|
||||
if (opacity == 0 && !clipper) {
|
||||
bbox.reset();
|
||||
bbox[0].reset();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -115,7 +115,7 @@ struct SwShapeTask : SwTask
|
|||
updateFill = (MULTIPLY(rshape->color.a, opacity) || rshape->fill);
|
||||
if (updateShape) shapeReset(&shape);
|
||||
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;
|
||||
} else {
|
||||
updateFill = false;
|
||||
|
@ -135,7 +135,7 @@ struct SwShapeTask : SwTask
|
|||
if (updateShape || flags & RenderUpdateFlag::Stroke) {
|
||||
if (strokeWidth > 0.0f) {
|
||||
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()) {
|
||||
auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false;
|
||||
if (ctable) shapeResetStrokeFill(&shape);
|
||||
|
@ -157,12 +157,11 @@ struct SwShapeTask : SwTask
|
|||
if (!clipShapeRle && !clipStrokeRle) goto err;
|
||||
}
|
||||
|
||||
bbox = renderBox; //sync
|
||||
|
||||
bbox[0] = renderBox; //sync
|
||||
return;
|
||||
|
||||
err:
|
||||
bbox.reset();
|
||||
bbox[0].reset();
|
||||
shapeReset(&shape);
|
||||
rleReset(shape.strokeRle);
|
||||
shapeDelOutline(&shape, mpool, tid);
|
||||
|
@ -188,7 +187,7 @@ struct SwImageTask : SwTask
|
|||
|
||||
void run(unsigned tid) override
|
||||
{
|
||||
auto clipBox = bbox;
|
||||
auto clipBox = bbox[0];
|
||||
|
||||
//Convert colorspace if it's not aligned.
|
||||
rasterConvertCS(source, surface->cs);
|
||||
|
@ -205,10 +204,10 @@ struct SwImageTask : SwTask
|
|||
imageReset(&image);
|
||||
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 (!imageGenRle(&image, bbox, false)) goto end;
|
||||
if (!imageGenRle(&image, bbox[0], false)) goto end;
|
||||
if (image.rle) {
|
||||
//Clear current task memorypool here if the clippers would use the same memory pool
|
||||
imageDelOutline(&image, mpool, tid);
|
||||
|
@ -222,6 +221,7 @@ struct SwImageTask : SwTask
|
|||
}
|
||||
goto end;
|
||||
err:
|
||||
bbox[0].reset();
|
||||
rleReset(image.rle);
|
||||
end:
|
||||
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 */
|
||||
/************************************************************************/
|
||||
|
@ -292,17 +267,20 @@ SwRenderer::~SwRenderer()
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::sync()
|
||||
{
|
||||
//clear if the rendering was not triggered.
|
||||
ARRAY_FOREACH(p, tasks) {
|
||||
if ((*p)->disposed) {
|
||||
delete(*p);
|
||||
} else {
|
||||
if ((*p)->disposed) delete(*p);
|
||||
else {
|
||||
(*p)->done();
|
||||
(*p)->pushed = false;
|
||||
}
|
||||
|
@ -360,7 +338,28 @@ bool SwRenderer::postUpdate()
|
|||
|
||||
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) {
|
||||
if ((*p)->disposed) delete(*p);
|
||||
else (*p)->pushed = false;
|
||||
else {
|
||||
(*p)->bbox[1] = (*p)->bbox[0];
|
||||
(*p)->pushed = false;
|
||||
}
|
||||
}
|
||||
tasks.clear();
|
||||
dirtyRegion.clear();
|
||||
|
||||
fulldraw = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -400,7 +405,19 @@ bool SwRenderer::renderImage(RenderData data)
|
|||
|
||||
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;
|
||||
|
||||
//Main raster stage
|
||||
if (task->rshape->strokeFirst()) {
|
||||
_renderStroke(task, surface);
|
||||
_renderFill(task, surface);
|
||||
auto fill = [](SwShapeTask* task, SwSurface* surface, const RenderRegion& bbox) {
|
||||
if (auto fill = task->rshape->fill) {
|
||||
rasterGradientShape(surface, &task->shape, bbox, 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, 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 {
|
||||
_renderFill(task, surface);
|
||||
_renderStroke(task, surface);
|
||||
//TODO: skip the stroke bbox if they are invalid.
|
||||
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;
|
||||
|
@ -480,6 +534,12 @@ bool SwRenderer::blend(BlendMethod method)
|
|||
}
|
||||
|
||||
|
||||
void SwRenderer::damage(const RenderRegion& region)
|
||||
{
|
||||
dirtyRegion.add(region);
|
||||
}
|
||||
|
||||
|
||||
RenderRegion SwRenderer::region(RenderData data)
|
||||
{
|
||||
return static_cast<SwTask*>(data)->bounds();
|
||||
|
@ -683,6 +743,9 @@ void SwRenderer::dispose(RenderData data)
|
|||
task->done();
|
||||
task->dispose();
|
||||
|
||||
//should be updated for the region; the current paint is removed
|
||||
dirtyRegion.add(task->bbox[0]);
|
||||
|
||||
if (task->pushed) task->disposed = true;
|
||||
else delete(task);
|
||||
}
|
||||
|
@ -703,7 +766,7 @@ void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Arr
|
|||
|
||||
task->surface = surface;
|
||||
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->clips = clips;
|
||||
task->opacity = opacity;
|
||||
|
|
|
@ -46,6 +46,7 @@ public:
|
|||
bool postRender() override;
|
||||
void dispose(RenderData data) override;
|
||||
RenderRegion region(RenderData data) override;
|
||||
void damage(const RenderRegion& region) override;
|
||||
RenderRegion viewport() override;
|
||||
bool viewport(const RenderRegion& vp) override;
|
||||
bool blend(BlendMethod method) override;
|
||||
|
@ -70,12 +71,14 @@ public:
|
|||
static bool term();
|
||||
|
||||
private:
|
||||
RenderDirtyRegion dirtyRegion;
|
||||
SwSurface* surface = nullptr; //active surface
|
||||
Array<SwTask*> tasks; //async task list
|
||||
Array<SwSurface*> compositors; //render targets cache list
|
||||
SwMpool* mpool; //private memory pool
|
||||
RenderRegion vport; //viewport
|
||||
bool sharedMpool; //memory-pool behavior policy
|
||||
bool fulldraw = true; //buffer is cleared (need to redraw full screen)
|
||||
|
||||
SwRenderer();
|
||||
~SwRenderer();
|
||||
|
|
|
@ -881,10 +881,10 @@ bool rleClip(SwRle *rle, const SwRle *clip)
|
|||
Array<SwSpan> out;
|
||||
out.reserve(std::max(rle->spans.count, clip->spans.count));
|
||||
|
||||
auto spans = rle->data();
|
||||
auto end = rle->spans.end();
|
||||
auto cspans = clip->data();
|
||||
auto cend = clip->spans.end();
|
||||
const SwSpan *end;
|
||||
auto spans = rle->fetch(clip->spans.first().y, clip->spans.last().y, &end);
|
||||
const SwSpan *cend;
|
||||
auto cspans = clip->fetch(rle->spans.first().y, rle->spans.last().y, &cend);
|
||||
|
||||
while(spans < end && cspans < cend) {
|
||||
//align y-coordinates.
|
||||
|
@ -928,9 +928,10 @@ bool rleClip(SwRle *rle, const RenderRegion* clip)
|
|||
Array<SwSpan> out;
|
||||
out.reserve(rle->spans.count);
|
||||
auto data = out.data;
|
||||
const SwSpan* end;
|
||||
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 < min.y || p->x >= max.x || (p->x + p->len) <= min.x) continue;
|
||||
if (p->x < min.x) {
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include "tvgMath.h"
|
||||
#include "tvgRender.h"
|
||||
|
||||
|
@ -116,6 +117,134 @@ void RenderRegion::intersect(const RenderRegion& rhs)
|
|||
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 */
|
||||
/************************************************************************/
|
||||
|
|
|
@ -50,7 +50,6 @@ static inline RenderUpdateFlag operator|(const RenderUpdateFlag a, const RenderU
|
|||
return RenderUpdateFlag(uint16_t(a) | uint16_t(b));
|
||||
}
|
||||
|
||||
|
||||
struct RenderSurface
|
||||
{
|
||||
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)}};
|
||||
}
|
||||
|
||||
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 add(const RenderRegion& rhs)
|
||||
|
@ -117,6 +121,16 @@ struct RenderRegion
|
|||
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
|
||||
{
|
||||
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(); }
|
||||
};
|
||||
|
||||
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
|
||||
{
|
||||
Array<PathCommand> cmds;
|
||||
|
@ -416,7 +492,7 @@ struct RenderEffectTritone : RenderEffect
|
|||
class RenderMethod
|
||||
{
|
||||
private:
|
||||
uint32_t refCnt = 0; //reference count
|
||||
uint32_t refCnt = 0;
|
||||
Key key;
|
||||
|
||||
public:
|
||||
|
@ -433,6 +509,7 @@ public:
|
|||
virtual bool renderImage(RenderData data) = 0;
|
||||
virtual bool postRender() = 0;
|
||||
virtual void dispose(RenderData data) = 0;
|
||||
virtual void damage(const RenderRegion& region) = 0;
|
||||
virtual RenderRegion region(RenderData data) = 0;
|
||||
virtual RenderRegion viewport() = 0;
|
||||
virtual bool viewport(const RenderRegion& vp) = 0;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
this->vport = renderer->viewport();
|
||||
vport = renderer->viewport();
|
||||
|
||||
if (needComposition(opacity)) {
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
@ -134,7 +137,7 @@ struct SceneImpl : Scene
|
|||
renderer->blend(impl.blendMethod);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -157,7 +160,7 @@ struct SceneImpl : Scene
|
|||
return ret;
|
||||
}
|
||||
|
||||
RenderRegion bounds(RenderMethod* renderer) const
|
||||
RenderRegion bounds(RenderMethod* renderer)
|
||||
{
|
||||
if (paints.empty()) return {};
|
||||
|
||||
|
@ -185,8 +188,8 @@ struct SceneImpl : Scene
|
|||
pRegion.max.x += eRegion.max.x;
|
||||
pRegion.max.y += eRegion.max.y;
|
||||
|
||||
pRegion.intersect(this->vport);
|
||||
return pRegion;
|
||||
vport = RenderRegion::intersect(renderer->viewport(), pRegion);
|
||||
return vport;
|
||||
}
|
||||
|
||||
Result bounds(Point* pt4, Matrix& m, bool obb, bool stroking)
|
||||
|
@ -298,6 +301,7 @@ struct SceneImpl : Scene
|
|||
}
|
||||
delete(effects);
|
||||
effects = nullptr;
|
||||
impl.renderer->damage(vport);
|
||||
}
|
||||
return Result::Success;
|
||||
}
|
||||
|
|
|
@ -274,6 +274,12 @@ void WgRenderer::dispose(RenderData data) {
|
|||
}
|
||||
|
||||
|
||||
void WgRenderer::damage(TVG_UNUSED const RenderRegion& region)
|
||||
{
|
||||
//TODO:
|
||||
}
|
||||
|
||||
|
||||
RenderRegion WgRenderer::region(RenderData data)
|
||||
{
|
||||
auto renderData = (WgRenderDataPaint*)data;
|
||||
|
|
|
@ -38,6 +38,7 @@ public:
|
|||
bool postRender() override;
|
||||
void dispose(RenderData data) override;
|
||||
RenderRegion region(RenderData data) override;
|
||||
void damage(const RenderRegion& region) override;
|
||||
RenderRegion viewport() override;
|
||||
bool viewport(const RenderRegion& vp) override;
|
||||
bool blend(BlendMethod method) override;
|
||||
|
|
Loading…
Add table
Reference in a new issue