sw_engine: enable render region clipping during rendering

Implemented support for clipping shapes and images using a render region
bounding box at render time. This allows partial drawing of content,
laying the groundwork for upcoming partial rendering functionality.

for fast access of the drawing region from the linear rle data,
we introduced the binary search for begin/end of rle instead of
additional y index buffer.

There is a reason for not using a y-index buffer:
the shapes in the RLE are not single, continuous shapes
but multiple shapes scattered across the space.

which means that we need a double-associated data structure
per shapes for y indexing, and this data preparation wouldn't be
cheaper enough than realtime binary search especially animated data.

This also helps for current clipping performance by utilizing
the introduced fast-clipping region access.

issue: https://github.com/thorvg/thorvg/issues/1747
This commit is contained in:
Hermet Park 2025-06-11 22:49:17 +09:00
parent 830db9ecb7
commit b22ceaae7c
7 changed files with 244 additions and 171 deletions

View file

@ -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 - 1, 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,15 +592,15 @@ 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 rasterShape(SwSurface* surface, SwShape* shape, const RenderRegion& bbox, RenderColor& c);
bool rasterTexmapPolygon(SwSurface* surface, const SwImage& image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity);
bool rasterScaledImage(SwSurface* surface, const SwImage& image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity);
bool rasterDirectImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, uint8_t opacity);
bool rasterScaledRleImage(SwSurface* surface, const SwImage& image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity);
bool rasterDirectRleImage(SwSurface* surface, const SwImage& image, uint8_t opacity);
bool rasterStroke(SwSurface* surface, SwShape* shape, RenderColor& c);
bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity);
bool rasterDirectRleImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, uint8_t opacity);
bool rasterStroke(SwSurface* surface, SwShape* shape, const RenderRegion& bbox, RenderColor& c);
bool rasterGradientShape(SwSurface* surface, SwShape* shape, const RenderRegion& bbox, 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);
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);

View file

@ -472,18 +472,21 @@ 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);
}
}
@ -491,18 +494,21 @@ static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRle* rle, SwMask mas
}
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 +517,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 +525,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 +559,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 +574,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 +600,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 +650,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;
}
@ -758,26 +774,29 @@ static bool _rasterScaledRleImage(SwSurface* surface, const SwImage& image, cons
/* 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));
}
@ -787,18 +806,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)));
}
@ -808,19 +831,23 @@ 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;
@ -1629,7 +1656,7 @@ bool rasterScaledRleImage(SwSurface* surface, const SwImage& image, const Matrix
}
bool rasterDirectRleImage(SwSurface* surface, const SwImage& image, uint8_t opacity)
bool rasterDirectRleImage(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!");
@ -1637,47 +1664,46 @@ bool rasterDirectRleImage(SwSurface* surface, const SwImage& image, uint8_t opac
}
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;
}
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();
@ -1687,19 +1713,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);
@ -1707,7 +1733,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);
}

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

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

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

View file

@ -204,9 +204,7 @@ struct SwImageTask : SwTask
if ((flags & (RenderUpdateFlag::Image | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) && (opacity > 0)) {
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, mpool, tid)) goto end;
if (clips.count > 0) {
if (!imageGenRle(&image, bbox, false)) goto end;
if (image.rle) {
@ -222,6 +220,7 @@ struct SwImageTask : SwTask
}
goto end;
err:
bbox.reset();
rleReset(image.rle);
end:
imageDelOutline(&image, mpool, tid);
@ -234,31 +233,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 */
/************************************************************************/
@ -299,10 +273,10 @@ bool SwRenderer::clear()
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;
}
@ -402,7 +376,7 @@ bool SwRenderer::renderImage(RenderData data)
//RLE Image
if (image.rle) {
if (image.direct) return rasterDirectRleImage(surface, image, task->opacity);
if (image.direct) return rasterDirectRleImage(surface, image, bbox, task->opacity);
else if (image.scaled) return rasterScaledRleImage(surface, image, task->transform, bbox, task->opacity);
else {
//create a intermediate buffer for rle clipping
@ -412,7 +386,7 @@ bool SwRenderer::renderImage(RenderData data)
cmp->compositor->image.rle = image.rle;
rasterClear(cmp, bbox.x(), bbox.y(), bbox.w(), bbox.h(), 0);
rasterTexmapPolygon(cmp, image, task->transform, bbox, 255);
return rasterDirectRleImage(surface, cmp->compositor->image, task->opacity);
return rasterDirectRleImage(surface, cmp->compositor->image, bbox, task->opacity);
}
//Whole Image
} else {
@ -432,13 +406,35 @@ bool SwRenderer::renderShape(RenderData data)
if (task->opacity == 0) return true;
//Main raster stage
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);
}
}
};
if (task->rshape->strokeFirst()) {
_renderStroke(task, surface);
_renderFill(task, surface);
stroke(task, surface, task->bbox);
fill(task, surface, task->shape.bbox);
} else {
_renderFill(task, surface);
_renderStroke(task, surface);
fill(task, surface, task->shape.bbox);
stroke(task, surface, task->bbox);
}
return true;

View file

@ -881,12 +881,18 @@ 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);
while(spans < end && cspans < cend) {
if (spans >= end) {
rle->spans.clear();
return false;
}
const SwSpan *cend;
auto cspans = clip->fetch(spans->y, (end - 1)->y, &cend);
while (spans < end && cspans < cend) {
//align y-coordinates.
if (cspans->y > spans->y) {
++spans;
@ -928,9 +934,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) {