sw_engine: revised the texture clipping
Some checks are pending
Android / build_x86_64 (push) Waiting to run
Android / build_aarch64 (push) Waiting to run
iOS / build_x86_64 (push) Waiting to run
iOS / build_arm64 (push) Waiting to run
macOS / build (push) Waiting to run
macOS / compact_test (push) Waiting to run
macOS / unit_test (push) Waiting to run
Ubuntu / build (push) Waiting to run
Ubuntu / compact_test (push) Waiting to run
Ubuntu / unit_test (push) Waiting to run
Windows / build (push) Waiting to run
Windows / compact_test (push) Waiting to run
Windows / unit_test (push) Waiting to run

previous logic doesn't work if the clipping shape
has any inner corners. replace the logic
with a intermediate composition approach for
stability.

- performance can be drop at texture clipping by ~11%
- size is reduced by -0.5kb

issue: https://github.com/thorvg/thorvg/issues/3520
This commit is contained in:
Hermet Park 2025-06-11 21:21:31 +09:00 committed by Hermet Park
parent 174fae9089
commit 3eaf110e0a
6 changed files with 216 additions and 306 deletions

View file

@ -1188,7 +1188,6 @@ void LottieBuilder::updateMasks(LottieLayer* layer, float frameNo)
auto method = mask->method; auto method = mask->method;
auto opacity = mask->opacity(frameNo); auto opacity = mask->opacity(frameNo);
auto expand = mask->expand(frameNo); auto expand = mask->expand(frameNo);
auto fastTrack = false; //single clipping
//the first mask //the first mask
if (!pShape) { if (!pShape) {
@ -1199,7 +1198,6 @@ void LottieBuilder::updateMasks(LottieLayer* layer, float frameNo)
if (layer->masks.count == 1 && compMethod == MaskMethod::Alpha) { if (layer->masks.count == 1 && compMethod == MaskMethod::Alpha) {
layer->scene->opacity(MULTIPLY(layer->scene->opacity(), opacity)); layer->scene->opacity(MULTIPLY(layer->scene->opacity(), opacity));
layer->scene->clip(pShape); layer->scene->clip(pShape);
fastTrack = true;
} else { } else {
layer->scene->mask(pShape, compMethod); layer->scene->mask(pShape, compMethod);
} }
@ -1225,9 +1223,6 @@ void LottieBuilder::updateMasks(LottieLayer* layer, float frameNo)
auto offset = LottieOffsetModifier(expand); auto offset = LottieOffsetModifier(expand);
mask->pathset(frameNo, SHAPE(pShape)->rs.path, nullptr, tween, exps, &offset); mask->pathset(frameNo, SHAPE(pShape)->rs.path, nullptr, tween, exps, &offset);
} }
if (fastTrack) return;
pOpacity = opacity; pOpacity = opacity;
pMethod = method; pMethod = method;
} }

View file

@ -568,7 +568,11 @@ void mpoolRetDashOutline(SwMpool* mpool, unsigned idx);
bool rasterCompositor(SwSurface* surface); bool rasterCompositor(SwSurface* surface);
bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity); bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity);
bool rasterShape(SwSurface* surface, SwShape* shape, RenderColor& c); bool rasterShape(SwSurface* surface, SwShape* shape, RenderColor& c);
bool rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity); 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 rasterStroke(SwSurface* surface, SwShape* shape, RenderColor& c);
bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity); bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity);
bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h, pixel_t val = 0); bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h, pixel_t val = 0);

View file

@ -220,10 +220,10 @@ static inline SwMask _getMaskOp(MaskMethod method)
} }
static bool _compositeMaskImage(SwSurface* surface, const SwImage* image, const RenderRegion& bbox) static bool _compositeMaskImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox)
{ {
auto dbuffer = &surface->buf8[bbox.min.y * surface->stride + bbox.min.x]; auto dbuffer = &surface->buf8[bbox.min.y * surface->stride + bbox.min.x];
auto sbuffer = image->buf8 + (bbox.min.y + image->oy) * image->stride + (bbox.min.x + image->ox); auto sbuffer = image.buf8 + (bbox.min.y + image.oy) * image.stride + (bbox.min.x + image.ox);
for (auto y = bbox.min.y; y < bbox.max.y; ++y) { for (auto y = bbox.min.y; y < bbox.max.y; ++y) {
auto dst = dbuffer; auto dst = dbuffer;
@ -232,7 +232,7 @@ static bool _compositeMaskImage(SwSurface* surface, const SwImage* image, const
*dst = *src + MULTIPLY(*dst, ~*src); *dst = *src + MULTIPLY(*dst, ~*src);
} }
dbuffer += surface->stride; dbuffer += surface->stride;
sbuffer += image->stride; sbuffer += image.stride;
} }
return true; return true;
} }
@ -330,7 +330,7 @@ static bool _rasterCompositeMaskedRect(SwSurface* surface, const RenderRegion& b
} }
cbuffer += cstride; cbuffer += cstride;
} }
return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); return _compositeMaskImage(surface, surface->compositor->image, surface->compositor->bbox);
} }
@ -487,7 +487,7 @@ static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRle* rle, SwMask mas
*cmp = maskOp(src, *cmp, ialpha); *cmp = maskOp(src, *cmp, ialpha);
} }
} }
return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); return _compositeMaskImage(surface, surface->compositor->image, surface->compositor->bbox);
} }
@ -657,44 +657,44 @@ static bool _rasterRle(SwSurface* surface, SwRle* rle, const RenderColor& c)
#define SCALED_IMAGE_RANGE_Y(y) \ #define SCALED_IMAGE_RANGE_Y(y) \
auto sy = (y) * itransform->e22 + itransform->e23 - 0.49f; \ auto sy = (y) * itransform->e22 + itransform->e23 - 0.49f; \
if (sy <= -0.5f || (uint32_t)(sy + 0.5f) >= image->h) continue; \ if (sy <= -0.5f || (uint32_t)(sy + 0.5f) >= image.h) continue; \
if (scaleMethod == _interpDownScaler) { \ if (scaleMethod == _interpDownScaler) { \
auto my = (int32_t)nearbyint(sy); \ auto my = (int32_t)nearbyint(sy); \
miny = my - (int32_t)sampleSize; \ miny = my - (int32_t)sampleSize; \
if (miny < 0) miny = 0; \ if (miny < 0) miny = 0; \
maxy = my + (int32_t)sampleSize; \ maxy = my + (int32_t)sampleSize; \
if (maxy >= (int32_t)image->h) maxy = (int32_t)image->h; \ if (maxy >= (int32_t)image.h) maxy = (int32_t)image.h; \
} }
#define SCALED_IMAGE_RANGE_X \ #define SCALED_IMAGE_RANGE_X \
auto sx = (x) * itransform->e11 + itransform->e13 - 0.49f; \ auto sx = (x) * itransform->e11 + itransform->e13 - 0.49f; \
if (sx <= -0.5f || (uint32_t)(sx + 0.5f) >= image->w) continue; \ if (sx <= -0.5f || (uint32_t)(sx + 0.5f) >= image.w) continue; \
static bool _rasterScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const RenderRegion& bbox, uint8_t opacity) static bool _rasterScaledMaskedRleImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const RenderRegion& bbox, uint8_t opacity)
{ {
TVGERR("SW_ENGINE", "Not Supported Scaled Masked(%d) Rle Image", (int)surface->compositor->method); TVGERR("SW_ENGINE", "Not Supported Scaled Masked(%d) Rle Image", (int)surface->compositor->method);
return false; return false;
} }
static bool _rasterScaledMattedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const RenderRegion& bbox, uint8_t opacity) static bool _rasterScaledMattedRleImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const RenderRegion& bbox, uint8_t opacity)
{ {
TVGLOG("SW_ENGINE", "Scaled Matted(%d) Rle Image", (int)surface->compositor->method); TVGLOG("SW_ENGINE", "Scaled Matted(%d) Rle Image", (int)surface->compositor->method);
auto csize = surface->compositor->image.channelSize; auto csize = surface->compositor->image.channelSize;
auto alpha = surface->alpha(surface->compositor->method); auto alpha = surface->alpha(surface->compositor->method);
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale); auto sampleSize = _sampleSize(image.scale);
int32_t miny = 0, maxy = 0; int32_t miny = 0, maxy = 0;
ARRAY_FOREACH(span, image->rle->spans) { ARRAY_FOREACH(span, image.rle->spans) {
SCALED_IMAGE_RANGE_Y(span->y) SCALED_IMAGE_RANGE_Y(span->y)
auto dst = &surface->buf32[span->y * surface->stride + span->x]; auto dst = &surface->buf32[span->y * surface->stride + span->x];
auto cmp = &surface->compositor->image.buf8[(span->y * surface->compositor->image.stride + span->x) * csize]; auto cmp = &surface->compositor->image.buf8[(span->y * surface->compositor->image.stride + span->x) * csize];
auto a = MULTIPLY(span->coverage, opacity); auto a = MULTIPLY(span->coverage, opacity);
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, cmp += csize) { for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, cmp += csize) {
SCALED_IMAGE_RANGE_X SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize);
src = ALPHA_BLEND(src, (a == 255) ? alpha(cmp) : MULTIPLY(alpha(cmp), a)); src = ALPHA_BLEND(src, (a == 255) ? alpha(cmp) : MULTIPLY(alpha(cmp), a));
*dst = src + ALPHA_BLEND(*dst, IA(src)); *dst = src + ALPHA_BLEND(*dst, IA(src));
} }
@ -703,27 +703,27 @@ static bool _rasterScaledMattedRleImage(SwSurface* surface, const SwImage* image
} }
static bool _rasterScaledBlendingRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const RenderRegion& bbox, uint8_t opacity) static bool _rasterScaledBlendingRleImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const RenderRegion& bbox, uint8_t opacity)
{ {
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale); auto sampleSize = _sampleSize(image.scale);
int32_t miny = 0, maxy = 0; int32_t miny = 0, maxy = 0;
ARRAY_FOREACH(span, image->rle->spans) { ARRAY_FOREACH(span, image.rle->spans) {
SCALED_IMAGE_RANGE_Y(span->y) SCALED_IMAGE_RANGE_Y(span->y)
auto dst = &surface->buf32[span->y * surface->stride + span->x]; auto dst = &surface->buf32[span->y * surface->stride + span->x];
auto alpha = MULTIPLY(span->coverage, opacity); auto alpha = MULTIPLY(span->coverage, opacity);
if (alpha == 255) { if (alpha == 255) {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) { for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
SCALED_IMAGE_RANGE_X SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize);
auto tmp = surface->blender(src, *dst, 255); auto tmp = surface->blender(src, *dst, 255);
*dst = INTERPOLATE(tmp, *dst, A(src)); *dst = INTERPOLATE(tmp, *dst, A(src));
} }
} else { } else {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) { for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
SCALED_IMAGE_RANGE_X SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize);
auto tmp = surface->blender(src, *dst, 255); auto tmp = surface->blender(src, *dst, 255);
*dst = INTERPOLATE(tmp, *dst, MULTIPLY(alpha, A(src))); *dst = INTERPOLATE(tmp, *dst, MULTIPLY(alpha, A(src)));
} }
@ -733,19 +733,19 @@ static bool _rasterScaledBlendingRleImage(SwSurface* surface, const SwImage* ima
} }
static bool _rasterScaledRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const RenderRegion& bbox, uint8_t opacity) static bool _rasterScaledRleImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const RenderRegion& bbox, uint8_t opacity)
{ {
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale); auto sampleSize = _sampleSize(image.scale);
int32_t miny = 0, maxy = 0; int32_t miny = 0, maxy = 0;
ARRAY_FOREACH(span, image->rle->spans) { ARRAY_FOREACH(span, image.rle->spans) {
SCALED_IMAGE_RANGE_Y(span->y) SCALED_IMAGE_RANGE_Y(span->y)
auto dst = &surface->buf32[span->y * surface->stride + span->x]; auto dst = &surface->buf32[span->y * surface->stride + span->x];
auto alpha = MULTIPLY(span->coverage, opacity); auto alpha = MULTIPLY(span->coverage, opacity);
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) { for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
SCALED_IMAGE_RANGE_X SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize);
if (alpha < 255) src = ALPHA_BLEND(src, alpha); if (alpha < 255) src = ALPHA_BLEND(src, alpha);
*dst = src + ALPHA_BLEND(*dst, IA(src)); *dst = src + ALPHA_BLEND(*dst, IA(src));
} }
@ -754,34 +754,11 @@ static bool _rasterScaledRleImage(SwSurface* surface, const SwImage* image, cons
} }
static bool _scaledRleImage(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 scaled rle image!");
return false;
}
Matrix itransform;
if (!inverse(&transform, &itransform)) return true;
if (_compositing(surface)) {
if (_matting(surface)) return _rasterScaledMattedRleImage(surface, image, &itransform, bbox, opacity);
else return _rasterScaledMaskedRleImage(surface, image, &itransform, bbox, opacity);
} else if (_blending(surface)) {
return _rasterScaledBlendingRleImage(surface, image, &itransform, bbox, opacity);
} else {
return _rasterScaledRleImage(surface, image, &itransform, bbox, opacity);
}
return false;
}
/************************************************************************/ /************************************************************************/
/* RLE Direct Image */ /* RLE Direct Image */
/************************************************************************/ /************************************************************************/
static bool _rasterDirectMattedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) static bool _rasterDirectMattedRleImage(SwSurface* surface, const SwImage& image, uint8_t opacity)
{ {
TVGLOG("SW_ENGINE", "Direct Matted(%d) Rle Image", (int)surface->compositor->method); TVGLOG("SW_ENGINE", "Direct Matted(%d) Rle Image", (int)surface->compositor->method);
@ -789,10 +766,10 @@ static bool _rasterDirectMattedRleImage(SwSurface* surface, const SwImage* image
auto cbuffer = surface->compositor->image.buf8; auto cbuffer = surface->compositor->image.buf8;
auto alpha = surface->alpha(surface->compositor->method); auto alpha = surface->alpha(surface->compositor->method);
ARRAY_FOREACH(span, image->rle->spans) { ARRAY_FOREACH(span, image.rle->spans) {
auto dst = &surface->buf32[span->y * surface->stride + span->x]; auto dst = &surface->buf32[span->y * surface->stride + span->x];
auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize]; 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); auto img = image.buf32 + (span->y + image.oy) * image.stride + (span->x + image.ox);
auto a = MULTIPLY(span->coverage, opacity); auto a = MULTIPLY(span->coverage, opacity);
if (a == 255) { if (a == 255) {
for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img, cmp += csize) { for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img, cmp += csize) {
@ -810,11 +787,11 @@ 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, uint8_t opacity)
{ {
ARRAY_FOREACH(span, image->rle->spans) { ARRAY_FOREACH(span, image.rle->spans) {
auto dst = &surface->buf32[span->y * surface->stride + span->x]; auto dst = &surface->buf32[span->y * surface->stride + span->x];
auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox); auto img = image.buf32 + (span->y + image.oy) * image.stride + (span->x + image.ox);
auto alpha = MULTIPLY(span->coverage, opacity); auto alpha = MULTIPLY(span->coverage, opacity);
if (alpha == 255) { if (alpha == 255) {
for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) {
@ -831,11 +808,11 @@ 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, uint8_t opacity)
{ {
ARRAY_FOREACH(span, image->rle->spans) { ARRAY_FOREACH(span, image.rle->spans) {
auto dst = &surface->buf32[span->y * surface->stride + span->x]; auto dst = &surface->buf32[span->y * surface->stride + span->x];
auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox); auto img = image.buf32 + (span->y + image.oy) * image.stride + (span->x + image.ox);
auto alpha = MULTIPLY(span->coverage, opacity); auto alpha = MULTIPLY(span->coverage, opacity);
rasterTranslucentPixel32(dst, img, span->len, alpha); rasterTranslucentPixel32(dst, img, span->len, alpha);
} }
@ -843,44 +820,25 @@ static bool _rasterDirectRleImage(SwSurface* surface, const SwImage* image, uint
} }
static bool _rasterDirectMaskedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) static bool _rasterDirectMaskedRleImage(SwSurface* surface, const SwImage& image, uint8_t opacity)
{ {
TVGERR("SW_ENGINE", "Not Supported Direct Masked(%d) Rle Image", (int)surface->compositor->method); TVGERR("SW_ENGINE", "Not Supported Direct Masked(%d) Rle Image", (int)surface->compositor->method);
return false; return false;
} }
static bool _directRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity)
{
if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale rle image!");
return false;
}
if (_compositing(surface)) {
if (_matting(surface)) return _rasterDirectMattedRleImage(surface, image, opacity);
else return _rasterDirectMaskedRleImage(surface, image, opacity);
} else if (_blending(surface)) {
return _rasterDirectBlendingRleImage(surface, image, opacity);
} else {
return _rasterDirectRleImage(surface, image, opacity);
}
return false;
}
/************************************************************************/ /************************************************************************/
/*Scaled Image */ /*Scaled Image */
/************************************************************************/ /************************************************************************/
static bool _rasterScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const RenderRegion& bbox, uint8_t opacity) static bool _rasterScaledMaskedImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const RenderRegion& bbox, uint8_t opacity)
{ {
TVGERR("SW_ENGINE", "Not Supported Scaled Masked Image!"); TVGERR("SW_ENGINE", "Not Supported Scaled Masked Image!");
return false; return false;
} }
static bool _rasterScaledMattedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const RenderRegion& bbox, uint8_t opacity) static bool _rasterScaledMattedImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const RenderRegion& bbox, uint8_t opacity)
{ {
if (surface->channelSize == sizeof(uint8_t)) { if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale scaled matted image!"); TVGERR("SW_ENGINE", "Not supported grayscale scaled matted image!");
@ -894,8 +852,8 @@ static bool _rasterScaledMattedImage(SwSurface* surface, const SwImage* image, c
TVGLOG("SW_ENGINE", "Scaled Matted(%d) Image [Region: %d %d %d %d]", (int)surface->compositor->method, bbox.min.x, bbox.min.y, bbox.max.x - bbox.min.x, bbox.max.y - bbox.min.y); TVGLOG("SW_ENGINE", "Scaled Matted(%d) Image [Region: %d %d %d %d]", (int)surface->compositor->method, bbox.min.x, bbox.min.y, bbox.max.x - bbox.min.x, bbox.max.y - bbox.min.y);
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale); auto sampleSize = _sampleSize(image.scale);
int32_t miny = 0, maxy = 0; int32_t miny = 0, maxy = 0;
for (auto y = bbox.min.y; y < bbox.max.y; ++y) { for (auto y = bbox.min.y; y < bbox.max.y; ++y) {
@ -904,7 +862,7 @@ static bool _rasterScaledMattedImage(SwSurface* surface, const SwImage* image, c
auto cmp = cbuffer; auto cmp = cbuffer;
for (auto x = bbox.min.x; x < bbox.max.x; ++x, ++dst, cmp += csize) { for (auto x = bbox.min.x; x < bbox.max.x; ++x, ++dst, cmp += csize) {
SCALED_IMAGE_RANGE_X SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize);
auto tmp = ALPHA_BLEND(src, opacity == 255 ? alpha(cmp) : MULTIPLY(opacity, alpha(cmp))); auto tmp = ALPHA_BLEND(src, opacity == 255 ? alpha(cmp) : MULTIPLY(opacity, alpha(cmp)));
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
} }
@ -915,7 +873,7 @@ static bool _rasterScaledMattedImage(SwSurface* surface, const SwImage* image, c
} }
static bool _rasterScaledBlendingImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const RenderRegion& bbox, uint8_t opacity) static bool _rasterScaledBlendingImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const RenderRegion& bbox, uint8_t opacity)
{ {
if (surface->channelSize == sizeof(uint8_t)) { if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale scaled blending image!"); TVGERR("SW_ENGINE", "Not supported grayscale scaled blending image!");
@ -923,8 +881,8 @@ static bool _rasterScaledBlendingImage(SwSurface* surface, const SwImage* image,
} }
auto dbuffer = surface->buf32 + (bbox.min.y * surface->stride + bbox.min.x); auto dbuffer = surface->buf32 + (bbox.min.y * surface->stride + bbox.min.x);
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale); auto sampleSize = _sampleSize(image.scale);
int32_t miny = 0, maxy = 0; int32_t miny = 0, maxy = 0;
for (auto y = bbox.min.y; y < bbox.max.y; ++y, dbuffer += surface->stride) { for (auto y = bbox.min.y; y < bbox.max.y; ++y, dbuffer += surface->stride) {
@ -932,7 +890,7 @@ static bool _rasterScaledBlendingImage(SwSurface* surface, const SwImage* image,
auto dst = dbuffer; auto dst = dbuffer;
for (auto x = bbox.min.x; x < bbox.max.x; ++x, ++dst) { for (auto x = bbox.min.x; x < bbox.max.x; ++x, ++dst) {
SCALED_IMAGE_RANGE_X SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize);
auto tmp = surface->blender(src, *dst, 255); auto tmp = surface->blender(src, *dst, 255);
*dst = INTERPOLATE(tmp, *dst, MULTIPLY(opacity, A(src))); *dst = INTERPOLATE(tmp, *dst, MULTIPLY(opacity, A(src)));
} }
@ -941,10 +899,10 @@ static bool _rasterScaledBlendingImage(SwSurface* surface, const SwImage* image,
} }
static bool _rasterScaledImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const RenderRegion& bbox, uint8_t opacity) static bool _rasterScaledImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const RenderRegion& bbox, uint8_t opacity)
{ {
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale); auto sampleSize = _sampleSize(image.scale);
int32_t miny = 0, maxy = 0; int32_t miny = 0, maxy = 0;
//32bits channels //32bits channels
@ -955,7 +913,7 @@ static bool _rasterScaledImage(SwSurface* surface, const SwImage* image, const M
auto dst = buffer; auto dst = buffer;
for (auto x = bbox.min.x; x < bbox.max.x; ++x, ++dst) { for (auto x = bbox.min.x; x < bbox.max.x; ++x, ++dst) {
SCALED_IMAGE_RANGE_X SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize);
if (opacity < 255) src = ALPHA_BLEND(src, opacity); if (opacity < 255) src = ALPHA_BLEND(src, opacity);
*dst = src + ALPHA_BLEND(*dst, IA(src)); *dst = src + ALPHA_BLEND(*dst, IA(src));
} }
@ -967,7 +925,7 @@ static bool _rasterScaledImage(SwSurface* surface, const SwImage* image, const M
auto dst = buffer; auto dst = buffer;
for (auto x = bbox.min.x; x < bbox.max.x; ++x, ++dst) { for (auto x = bbox.min.x; x < bbox.max.x; ++x, ++dst) {
SCALED_IMAGE_RANGE_X SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize);
*dst = MULTIPLY(A(src), opacity); *dst = MULTIPLY(A(src), opacity);
} }
} }
@ -976,40 +934,22 @@ static bool _rasterScaledImage(SwSurface* surface, const SwImage* image, const M
} }
static bool _scaledImage(SwSurface* surface, const SwImage* image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity)
{
Matrix itransform;
if (!inverse(&transform, &itransform)) return true;
if (_compositing(surface)) {
if (_matting(surface)) return _rasterScaledMattedImage(surface, image, &itransform, bbox, opacity);
else return _rasterScaledMaskedImage(surface, image, &itransform, bbox, opacity);
} else if (_blending(surface)) {
return _rasterScaledBlendingImage(surface, image, &itransform, bbox, opacity);
} else {
return _rasterScaledImage(surface, image, &itransform, bbox, opacity);
}
return false;
}
/************************************************************************/ /************************************************************************/
/* Direct Image */ /* Direct Image */
/************************************************************************/ /************************************************************************/
static bool _rasterDirectMaskedImage(SwSurface* surface, const SwImage* image, const RenderRegion& bbox, uint8_t opacity) static bool _rasterDirectMaskedImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, uint8_t opacity)
{ {
TVGERR("SW_ENGINE", "Not Supported: Direct Masked Image"); TVGERR("SW_ENGINE", "Not Supported: Direct Masked Image");
return false; return false;
} }
static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage* image, const RenderRegion& bbox, uint8_t opacity) static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, uint8_t opacity)
{ {
auto csize = surface->compositor->image.channelSize; auto csize = surface->compositor->image.channelSize;
auto alpha = surface->alpha(surface->compositor->method); auto alpha = surface->alpha(surface->compositor->method);
auto sbuffer = image->buf32 + (bbox.min.y + image->oy) * image->stride + (bbox.min.x + image->ox); auto sbuffer = image.buf32 + (bbox.min.y + image.oy) * image.stride + (bbox.min.x + image.ox);
auto cbuffer = surface->compositor->image.buf8 + (bbox.min.y * surface->compositor->image.stride + bbox.min.x) * csize; //compositor buffer auto cbuffer = surface->compositor->image.buf8 + (bbox.min.y * surface->compositor->image.stride + bbox.min.x) * csize; //compositor buffer
TVGLOG("SW_ENGINE", "Direct Matted(%d) Image [Region: %u %u %u %u]", (int)surface->compositor->method, bbox.x(), bbox.y(), bbox.w(), bbox.h()); TVGLOG("SW_ENGINE", "Direct Matted(%d) Image [Region: %u %u %u %u]", (int)surface->compositor->method, bbox.x(), bbox.y(), bbox.w(), bbox.h());
@ -1034,7 +974,7 @@ static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage* image, c
} }
buffer += surface->stride; buffer += surface->stride;
cbuffer += surface->compositor->image.stride * csize; cbuffer += surface->compositor->image.stride * csize;
sbuffer += image->stride; sbuffer += image.stride;
} }
//8 bits //8 bits
} else if (surface->channelSize == sizeof(uint8_t)) { } else if (surface->channelSize == sizeof(uint8_t)) {
@ -1056,14 +996,14 @@ static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage* image, c
} }
buffer += surface->stride; buffer += surface->stride;
cbuffer += surface->compositor->image.stride * csize; cbuffer += surface->compositor->image.stride * csize;
sbuffer += image->stride; sbuffer += image.stride;
} }
} }
return true; return true;
} }
static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage* image, const RenderRegion& bbox, uint8_t opacity) static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, uint8_t opacity)
{ {
if (surface->channelSize == sizeof(uint8_t)) { if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale image!"); TVGERR("SW_ENGINE", "Not supported grayscale image!");
@ -1071,7 +1011,7 @@ static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage* image,
} }
auto dbuffer = &surface->buf32[bbox.min.y * surface->stride + bbox.min.x]; auto dbuffer = &surface->buf32[bbox.min.y * surface->stride + bbox.min.x];
auto sbuffer = image->buf32 + (bbox.min.y + image->oy) * image->stride + (bbox.min.x + image->ox); auto sbuffer = image.buf32 + (bbox.min.y + image.oy) * image.stride + (bbox.min.x + image.ox);
for (auto y = bbox.min.y; y < bbox.max.y; ++y) { for (auto y = bbox.min.y; y < bbox.max.y; ++y) {
auto dst = dbuffer; auto dst = dbuffer;
@ -1088,15 +1028,15 @@ static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage* image,
} }
} }
dbuffer += surface->stride; dbuffer += surface->stride;
sbuffer += image->stride; sbuffer += image.stride;
} }
return true; return true;
} }
static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const RenderRegion& bbox, uint8_t opacity) static bool _rasterDirectImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, uint8_t opacity)
{ {
auto sbuffer = image->buf32 + (bbox.min.y + image->oy) * image->stride + (bbox.min.x + image->ox); auto sbuffer = image.buf32 + (bbox.min.y + image.oy) * image.stride + (bbox.min.x + image.ox);
//32bits channels //32bits channels
if (surface->channelSize == sizeof(uint32_t)) { if (surface->channelSize == sizeof(uint32_t)) {
@ -1104,12 +1044,12 @@ static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const R
for (auto y = bbox.min.y; y < bbox.max.y; ++y) { for (auto y = bbox.min.y; y < bbox.max.y; ++y) {
rasterTranslucentPixel32(dbuffer, sbuffer, bbox.max.x - bbox.min.x, opacity); rasterTranslucentPixel32(dbuffer, sbuffer, bbox.max.x - bbox.min.x, opacity);
dbuffer += surface->stride; dbuffer += surface->stride;
sbuffer += image->stride; sbuffer += image.stride;
} }
//8bits grayscale //8bits grayscale
} else if (surface->channelSize == sizeof(uint8_t)) { } else if (surface->channelSize == sizeof(uint8_t)) {
auto dbuffer = &surface->buf8[bbox.min.y * surface->stride + bbox.min.x]; auto dbuffer = &surface->buf8[bbox.min.y * surface->stride + bbox.min.x];
for (auto y = bbox.min.y; y < bbox.max.y; ++y, dbuffer += surface->stride, sbuffer += image->stride) { for (auto y = bbox.min.y; y < bbox.max.y; ++y, dbuffer += surface->stride, sbuffer += image.stride) {
auto dst = dbuffer; auto dst = dbuffer;
auto src = sbuffer; auto src = sbuffer;
if (opacity == 255) { if (opacity == 255) {
@ -1127,7 +1067,7 @@ static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const R
} }
static bool _rasterDirectMattedBlendingImage(SwSurface* surface, const SwImage* image, const RenderRegion& bbox, uint8_t opacity) static bool _rasterDirectMattedBlendingImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, uint8_t opacity)
{ {
if (surface->channelSize == sizeof(uint8_t)) { if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale image!"); TVGERR("SW_ENGINE", "Not supported grayscale image!");
@ -1136,7 +1076,7 @@ static bool _rasterDirectMattedBlendingImage(SwSurface* surface, const SwImage*
auto csize = surface->compositor->image.channelSize; auto csize = surface->compositor->image.channelSize;
auto alpha = surface->alpha(surface->compositor->method); auto alpha = surface->alpha(surface->compositor->method);
auto sbuffer = image->buf32 + (bbox.min.y + image->oy) * image->stride + (bbox.min.x + image->ox); auto sbuffer = image.buf32 + (bbox.min.y + image.oy) * image.stride + (bbox.min.x + image.ox);
auto cbuffer = surface->compositor->image.buf8 + (bbox.min.y * surface->compositor->image.stride + bbox.min.x) * csize; //compositor buffer auto cbuffer = surface->compositor->image.buf8 + (bbox.min.y * surface->compositor->image.stride + bbox.min.x) * csize; //compositor buffer
auto buffer = surface->buf32 + (bbox.min.y * surface->stride) + bbox.min.x; auto buffer = surface->buf32 + (bbox.min.y * surface->stride) + bbox.min.x;
@ -1157,46 +1097,12 @@ static bool _rasterDirectMattedBlendingImage(SwSurface* surface, const SwImage*
} }
buffer += surface->stride; buffer += surface->stride;
cbuffer += surface->compositor->image.stride * csize; cbuffer += surface->compositor->image.stride * csize;
sbuffer += image->stride; sbuffer += image.stride;
} }
return true; return true;
} }
//Blenders for the following scenarios: [Composition / Non-Composition] * [Opaque / Translucent]
static bool _directImage(SwSurface* surface, const SwImage* image, const RenderRegion& bbox, uint8_t opacity)
{
if (_compositing(surface)) {
if (_matting(surface)) {
if (_blending(surface)) return _rasterDirectMattedBlendingImage(surface, image, bbox, opacity);
else return _rasterDirectMattedImage(surface, image, bbox, opacity);
} else return _rasterDirectMaskedImage(surface, image, bbox, opacity);
} else if (_blending(surface)) {
return _rasterDirectBlendingImage(surface, image, bbox, opacity);
} else {
return _rasterDirectImage(surface, image, bbox, opacity);
}
return false;
}
//Blenders for the following scenarios: [RLE / Whole] * [Direct / Scaled / Transformed]
static bool _rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity)
{
//RLE Image
if (image->rle) {
if (image->direct) return _directRleImage(surface, image, opacity);
else if (image->scaled) return _scaledRleImage(surface, image, transform, bbox, opacity);
else return _rasterTexmapPolygon(surface, image, transform, nullptr, 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);
}
}
/************************************************************************/ /************************************************************************/
/* Rect Gradient */ /* Rect Gradient */
/************************************************************************/ /************************************************************************/
@ -1211,7 +1117,7 @@ static bool _rasterCompositeGradientMaskedRect(SwSurface* surface, const RenderR
fillMethod()(fill, cbuffer, bbox.min.y + y, bbox.min.x, bbox.w(), maskOp, 255); fillMethod()(fill, cbuffer, bbox.min.y + y, bbox.min.x, bbox.w(), maskOp, 255);
cbuffer += surface->stride; cbuffer += surface->stride;
} }
return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); return _compositeMaskImage(surface, surface->compositor->image, surface->compositor->bbox);
} }
@ -1372,7 +1278,7 @@ static bool _rasterCompositeGradientMaskedRle(SwSurface* surface, const SwRle* r
auto cmp = &cbuffer[span->y * cstride + span->x]; auto cmp = &cbuffer[span->y * cstride + span->x];
fillMethod()(fill, cmp, span->y, span->x, span->len, maskOp, span->coverage); fillMethod()(fill, cmp, span->y, span->x, span->len, maskOp, span->coverage);
} }
return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); return _compositeMaskImage(surface, surface->compositor->image, surface->compositor->bbox);
} }
@ -1666,6 +1572,82 @@ void rasterPremultiply(RenderSurface* surface)
} }
bool rasterScaledImage(SwSurface* surface, const SwImage& image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity)
{
Matrix itransform;
if (!inverse(&transform, &itransform)) return true;
if (_compositing(surface)) {
if (_matting(surface)) return _rasterScaledMattedImage(surface, image, &itransform, bbox, opacity);
else return _rasterScaledMaskedImage(surface, image, &itransform, bbox, opacity);
} else if (_blending(surface)) {
return _rasterScaledBlendingImage(surface, image, &itransform, bbox, opacity);
} else {
return _rasterScaledImage(surface, image, &itransform, bbox, opacity);
}
return false;
}
bool rasterDirectImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, uint8_t opacity)
{
if (_compositing(surface)) {
if (_matting(surface)) {
if (_blending(surface)) return _rasterDirectMattedBlendingImage(surface, image, bbox, opacity);
else return _rasterDirectMattedImage(surface, image, bbox, opacity);
} else return _rasterDirectMaskedImage(surface, image, bbox, opacity);
} else if (_blending(surface)) {
return _rasterDirectBlendingImage(surface, image, bbox, opacity);
} else {
return _rasterDirectImage(surface, image, bbox, opacity);
}
return false;
}
bool rasterScaledRleImage(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 scaled rle image!");
return false;
}
Matrix itransform;
if (!inverse(&transform, &itransform)) return true;
if (_compositing(surface)) {
if (_matting(surface)) return _rasterScaledMattedRleImage(surface, image, &itransform, bbox, opacity);
else return _rasterScaledMaskedRleImage(surface, image, &itransform, bbox, opacity);
} else if (_blending(surface)) {
return _rasterScaledBlendingRleImage(surface, image, &itransform, bbox, opacity);
} else {
return _rasterScaledRleImage(surface, image, &itransform, bbox, opacity);
}
return false;
}
bool rasterDirectRleImage(SwSurface* surface, const SwImage& image, uint8_t opacity)
{
if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale rle image!");
return false;
}
if (_compositing(surface)) {
if (_matting(surface)) return _rasterDirectMattedRleImage(surface, image, opacity);
else return _rasterDirectMaskedRleImage(surface, image, opacity);
} else if (_blending(surface)) {
return _rasterDirectBlendingRleImage(surface, image, opacity);
} else {
return _rasterDirectRleImage(surface, image, opacity);
}
return false;
}
bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity) bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity)
{ {
if (!shape->fill) return false; if (!shape->fill) return false;
@ -1729,15 +1711,6 @@ bool rasterStroke(SwSurface* surface, SwShape* shape, RenderColor& c)
} }
bool rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity)
{
//Outside of the viewport, skip the rendering
if (bbox.max.x < 0 || bbox.max.y < 0 || bbox.min.x >= static_cast<SwCoord>(surface->w) || bbox.min.y >= static_cast<SwCoord>(surface->h)) return true;
return _rasterImage(surface, image, transform, bbox, opacity);
}
bool rasterConvertCS(RenderSurface* surface, ColorSpace to) bool rasterConvertCS(RenderSurface* surface, ColorSpace to)
{ {
ScopedLock lock(surface->key); ScopedLock lock(surface->key);

View file

@ -49,89 +49,41 @@ static float dxdya, dxdyb, dudya, dvdya;
static float xa, xb, ua, va; static float xa, xb, ua, va;
//Y Range exception handling
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;
return yEnd > yStart;
}
static inline int32_t _modf(float v) static inline int32_t _modf(float v)
{ {
return 255 - ((int(v * 256.0f)) & 255); return 255 - ((int(v * 256.0f)) & 255);
} }
static bool _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* image, const RenderRegion* bbox, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0) static bool _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0)
{ {
TVGERR("SW_ENGINE", "TODO: _rasterMaskedPolygonImageSegment()"); TVGERR("SW_ENGINE", "TODO: _rasterMaskedPolygonImageSegment()");
return false; return false;
} }
static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage* image, const RenderRegion* bbox, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity) static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity)
{ {
float _dudx = dudx, _dvdx = dvdx; float _dudx = dudx, _dvdx = dvdx;
float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya; float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
float _xa = xa, _xb = xb, _ua = ua, _va = va; float _xa = xa, _xb = xb, _ua = ua, _va = va;
auto sbuf = image->buf32; auto sbuf = image.buf32;
auto dbuf = surface->buf32; auto dbuf = surface->buf32;
int32_t sw = static_cast<int32_t>(image->w); int32_t sw = static_cast<int32_t>(image.w);
int32_t sh = static_cast<int32_t>(image->h); int32_t sh = static_cast<int32_t>(image.h);
int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay; int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
int32_t vv = 0, uu = 0; int32_t vv = 0, uu = 0;
int32_t minx = INT32_MAX, maxx = 0;
float dx, u, v; float dx, u, v;
uint32_t* buf; uint32_t* buf;
SwSpan* span = nullptr; //used only when rle based.
if (!_arrange(image, bbox, yStart, yEnd)) return; if (yStart < bbox.min.y) yStart = bbox.min.y;
if (yEnd > bbox.max.y) yEnd = bbox.max.y;
//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;
}
}
y = yStart; y = yStart;
while (y < yEnd) { while (y < yEnd) {
x1 = (int32_t)_xa; x1 = std::max((int32_t)_xa, bbox.min.x);
x2 = (int32_t)_xb; x2 = std::min((int32_t)_xb, bbox.max.x);
if (!bbox) {
minx = INT32_MAX;
maxx = 0;
//one single row, could be consisted of multiple spans.
while (span->y == y && spanIdx < image->rle->size()) {
if (minx > span->x) minx = span->x;
if (maxx < span->x + span->len) maxx = span->x + span->len;
++span;
++spanIdx;
}
}
if (x1 < minx) x1 = minx;
if (x2 > maxx) x2 = maxx;
//Anti-Aliasing frames //Anti-Aliasing frames
if (aaSpans) { if (aaSpans) {
@ -141,7 +93,7 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
} }
//Range allowed //Range allowed
if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) { if ((x2 - x1) >= 1 && (x1 < bbox.max.x) && (x2 > bbox.min.x)) {
//Perform subtexel pre-stepping on UV //Perform subtexel pre-stepping on UV
dx = 1 - (_xa - x1); dx = 1 - (_xa - x1);
@ -155,29 +107,29 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
uu = (int) u; uu = (int) u;
vv = (int) v; vv = (int) v;
if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue; if ((uint32_t) uu >= image.w || (uint32_t) vv >= image.h) continue;
ar = _modf(u); ar = _modf(u);
ab = _modf(v); ab = _modf(v);
iru = uu + 1; iru = uu + 1;
irv = vv + 1; irv = vv + 1;
px = *(sbuf + (vv * image->stride) + uu); px = *(sbuf + (vv * image.stride) + uu);
/* horizontal interpolate */ /* horizontal interpolate */
if (iru < sw) { if (iru < sw) {
/* right pixel */ /* right pixel */
int px2 = *(sbuf + (vv * image->stride) + iru); int px2 = *(sbuf + (vv * image.stride) + iru);
px = INTERPOLATE(px, px2, ar); px = INTERPOLATE(px, px2, ar);
} }
/* vertical interpolate */ /* vertical interpolate */
if (irv < sh) { if (irv < sh) {
/* bottom pixel */ /* bottom pixel */
int px2 = *(sbuf + (irv * image->stride) + uu); int px2 = *(sbuf + (irv * image.stride) + uu);
/* horizontal interpolate */ /* horizontal interpolate */
if (iru < sw) { if (iru < sw) {
/* bottom right pixel */ /* bottom right pixel */
int px3 = *(sbuf + (irv * image->stride) + iru); int px3 = *(sbuf + (irv * image.stride) + iru);
px2 = INTERPOLATE(px2, px3, ar); px2 = INTERPOLATE(px2, px3, ar);
} }
px = INTERPOLATE(px, px2, ab); px = INTERPOLATE(px, px2, ab);
@ -198,8 +150,6 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
_ua += _dudya; _ua += _dudya;
_va += _dvdya; _va += _dvdya;
if (!bbox && spanIdx >= image->rle->size()) break;
++y; ++y;
} }
xa = _xa; xa = _xa;
@ -209,62 +159,33 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
} }
static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const RenderRegion* bbox, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, bool matting) static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, bool matting)
{ {
float _dudx = dudx, _dvdx = dvdx; float _dudx = dudx, _dvdx = dvdx;
float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya; float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
float _xa = xa, _xb = xb, _ua = ua, _va = va; float _xa = xa, _xb = xb, _ua = ua, _va = va;
auto sbuf = image->buf32; auto sbuf = image.buf32;
auto dbuf = surface->buf32; auto dbuf = surface->buf32;
int32_t sw = static_cast<int32_t>(image->w); int32_t sw = static_cast<int32_t>(image.w);
int32_t sh = static_cast<int32_t>(image->h); int32_t sh = static_cast<int32_t>(image.h);
int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay; int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
int32_t vv = 0, uu = 0; int32_t vv = 0, uu = 0;
int32_t minx = INT32_MAX, maxx = 0;
float dx, u, v; float dx, u, v;
uint32_t* buf; uint32_t* buf;
SwSpan* span = nullptr; //used only when rle based.
//for matting(composition) //for matting(composition)
auto csize = matting ? surface->compositor->image.channelSize: 0; auto csize = matting ? surface->compositor->image.channelSize: 0;
auto alpha = matting ? surface->alpha(surface->compositor->method) : nullptr; auto alpha = matting ? surface->alpha(surface->compositor->method) : nullptr;
uint8_t* cmp = nullptr; uint8_t* cmp = nullptr;
if (!_arrange(image, bbox, yStart, yEnd)) return; if (yStart < bbox.min.y) yStart = bbox.min.y;
if (yEnd > bbox.max.y) yEnd = bbox.max.y;
//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;
}
}
y = yStart; y = yStart;
while (y < yEnd) { while (y < yEnd) {
x1 = (int32_t)_xa; x1 = std::max((int32_t)_xa, bbox.min.x);
x2 = (int32_t)_xb; x2 = std::min((int32_t)_xb, bbox.max.x);
if (!bbox) {
minx = INT32_MAX;
maxx = 0;
//one single row, could be consisted of multiple spans.
while (span->y == y && spanIdx < image->rle->size()) {
if (minx > span->x) minx = span->x;
if (maxx < span->x + span->len) maxx = span->x + span->len;
++span;
++spanIdx;
}
}
if (x1 < minx) x1 = minx;
if (x2 > maxx) x2 = maxx;
//Anti-Aliasing frames //Anti-Aliasing frames
if (aaSpans) { if (aaSpans) {
@ -274,7 +195,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
} }
//Range allowed //Range allowed
if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) { if ((x2 - x1) >= 1 && (x1 < bbox.max.x) && (x2 > bbox.min.x)) {
//Perform subtexel pre-stepping on UV //Perform subtexel pre-stepping on UV
dx = 1 - (_xa - x1); dx = 1 - (_xa - x1);
@ -292,7 +213,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
uu = (int) u; uu = (int) u;
vv = (int) v; vv = (int) v;
if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue; if ((uint32_t) uu >= image.w || (uint32_t) vv >= image.h) continue;
ar = _modf(u); ar = _modf(u);
ab = _modf(v); ab = _modf(v);
@ -300,23 +221,23 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
iru = uu + 1; iru = uu + 1;
irv = vv + 1; irv = vv + 1;
px = *(sbuf + (vv * image->stride) + uu); px = *(sbuf + (vv * image.stride) + uu);
/* horizontal interpolate */ /* horizontal interpolate */
if (iru < sw) { if (iru < sw) {
/* right pixel */ /* right pixel */
int px2 = *(sbuf + (vv * image->stride) + iru); int px2 = *(sbuf + (vv * image.stride) + iru);
px = INTERPOLATE(px, px2, ar); px = INTERPOLATE(px, px2, ar);
} }
/* vertical interpolate */ /* vertical interpolate */
if (irv < sh) { if (irv < sh) {
/* bottom pixel */ /* bottom pixel */
int px2 = *(sbuf + (irv * image->stride) + uu); int px2 = *(sbuf + (irv * image.stride) + uu);
/* horizontal interpolate */ /* horizontal interpolate */
if (iru < sw) { if (iru < sw) {
/* bottom right pixel */ /* bottom right pixel */
int px3 = *(sbuf + (irv * image->stride) + iru); int px3 = *(sbuf + (irv * image.stride) + iru);
px2 = INTERPOLATE(px2, px3, ar); px2 = INTERPOLATE(px2, px3, ar);
} }
px = INTERPOLATE(px, px2, ab); px = INTERPOLATE(px, px2, ab);
@ -344,8 +265,6 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
_ua += _dudya; _ua += _dudya;
_va += _dvdya; _va += _dvdya;
if (!bbox && spanIdx >= image->rle->size()) break;
++y; ++y;
} }
xa = _xa; xa = _xa;
@ -356,7 +275,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
/* This mapping algorithm is based on Mikael Kalms's. */ /* This mapping algorithm is based on Mikael Kalms's. */
static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const RenderRegion* bbox, Polygon& polygon, AASpans* aaSpans, uint8_t opacity) static void _rasterPolygonImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, Polygon& polygon, AASpans* aaSpans, uint8_t opacity)
{ {
float x[3] = {polygon.vertex[0].pt.x, polygon.vertex[1].pt.x, polygon.vertex[2].pt.x}; float x[3] = {polygon.vertex[0].pt.x, polygon.vertex[1].pt.x, polygon.vertex[2].pt.x};
float y[3] = {polygon.vertex[0].pt.y, polygon.vertex[1].pt.y, polygon.vertex[2].pt.y}; float y[3] = {polygon.vertex[0].pt.y, polygon.vertex[1].pt.y, polygon.vertex[2].pt.y};
@ -417,7 +336,6 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
if (tvg::equal(y[0], y[1])) side = x[0] > x[1]; if (tvg::equal(y[0], y[1])) side = x[0] > x[1];
if (tvg::equal(y[1], y[2])) side = x[2] > x[1]; if (tvg::equal(y[1], y[2])) side = x[2] > x[1];
auto bboxTop = bbox ? bbox->min.y : image->rle->data()->y; //Normal Image or Rle Image?
auto compositing = _compositing(surface); //Composition required auto compositing = _compositing(surface); //Composition required
auto blending = _blending(surface); //Blending required auto blending = _blending(surface); //Blending required
@ -436,7 +354,7 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
//Draw upper segment if possibly visible //Draw upper segment if possibly visible
if (yi[0] < yi[1]) { if (yi[0] < yi[1]) {
off_y = y[0] < bboxTop ? (bboxTop - y[0]) : 0; off_y = y[0] < bbox.min.y ? (bbox.min.y - y[0]) : 0;
xa += (off_y * dxdya); xa += (off_y * dxdya);
ua += (off_y * dudya); ua += (off_y * dudya);
va += (off_y * dvdya); va += (off_y * dvdya);
@ -457,7 +375,7 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
} }
//Draw lower segment if possibly visible //Draw lower segment if possibly visible
if (yi[1] < yi[2]) { if (yi[1] < yi[2]) {
off_y = y[1] < bboxTop ? (bboxTop - y[1]) : 0; off_y = y[1] < bbox.min.y ? (bbox.min.y - y[1]) : 0;
if (!upper) { if (!upper) {
xa += (off_y * dxdya); xa += (off_y * dxdya);
ua += (off_y * dudya); ua += (off_y * dudya);
@ -484,7 +402,7 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
//Draw upper segment if possibly visible //Draw upper segment if possibly visible
if (yi[0] < yi[1]) { if (yi[0] < yi[1]) {
off_y = y[0] < bboxTop ? (bboxTop - y[0]) : 0; off_y = y[0] < bbox.min.y ? (bbox.min.y - y[0]) : 0;
xb += (off_y *dxdyb); xb += (off_y *dxdyb);
// Set slopes along left edge and perform subpixel pre-stepping // Set slopes along left edge and perform subpixel pre-stepping
@ -508,7 +426,7 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
} }
//Draw lower segment if possibly visible //Draw lower segment if possibly visible
if (yi[1] < yi[2]) { if (yi[1] < yi[2]) {
off_y = y[1] < bboxTop ? (bboxTop - y[1]) : 0; off_y = y[1] < bbox.min.y ? (bbox.min.y - y[1]) : 0;
if (!upper) xb += (off_y *dxdyb); if (!upper) xb += (off_y *dxdyb);
// Set slopes along left edge and perform subpixel pre-stepping // Set slopes along left edge and perform subpixel pre-stepping
@ -599,22 +517,19 @@ static void _apply(SwSurface* surface, AASpans* aaSpans)
| / | | / |
3 -- 2 3 -- 2
*/ */
static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix& transform, const RenderRegion* bbox, uint8_t opacity) bool rasterTexmapPolygon(SwSurface* surface, const SwImage& image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity)
{ {
if (surface->channelSize == sizeof(uint8_t)) { if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!"); TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!");
return false; return false;
} }
//Exceptions: No dedicated drawing area?
if ((!image->rle && !bbox) || (image->rle && image->rle->size() == 0)) return true;
//Prepare vertices. Shift XY coordinates to match the sub-pixeling technique. //Prepare vertices. Shift XY coordinates to match the sub-pixeling technique.
Vertex vertices[4]; Vertex vertices[4];
vertices[0] = {{0.0f, 0.0f}, {0.0f, 0.0f}}; vertices[0] = {{0.0f, 0.0f}, {0.0f, 0.0f}};
vertices[1] = {{float(image->w), 0.0f}, {float(image->w), 0.0f}}; vertices[1] = {{float(image.w), 0.0f}, {float(image.w), 0.0f}};
vertices[2] = {{float(image->w), float(image->h)}, {float(image->w), float(image->h)}}; vertices[2] = {{float(image.w), float(image.h)}, {float(image.w), float(image.h)}};
vertices[3] = {{0.0f, float(image->h)}, {0.0f, float(image->h)}}; vertices[3] = {{0.0f, float(image.h)}, {0.0f, float(image.h)}};
float ys = FLT_MAX, ye = -1.0f; float ys = FLT_MAX, ye = -1.0f;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
@ -623,10 +538,8 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
if (vertices[i].pt.y > ye) ye = vertices[i].pt.y; if (vertices[i].pt.y > ye) ye = vertices[i].pt.y;
} }
auto yStart = static_cast<int>(ys); auto yStart = std::max(static_cast<int>(ys), bbox.min.y);
auto yEnd = static_cast<int>(ye); auto yEnd = std::min(static_cast<int>(ye), bbox.max.y);
if (!_arrange(image, bbox, yStart, yEnd)) return true;
auto aaSpans = rightAngle(transform) ? nullptr : _AASpans(yStart, yEnd); auto aaSpans = rightAngle(transform) ? nullptr : _AASpans(yStart, yEnd);
Polygon polygon; Polygon polygon;

View file

@ -400,7 +400,32 @@ bool SwRenderer::renderImage(RenderData data)
if (task->opacity == 0) return true; if (task->opacity == 0) return true;
return rasterImage(surface, &task->image, task->transform, task->bbox, task->opacity); //Outside of the viewport, skip the rendering
auto& bbox = task->bbox;
if (bbox.invalid() || bbox.x() >= surface->w || bbox.y() >= surface->h) return true;
auto& image = task->image;
//RLE Image
if (image.rle) {
if (image.direct) return rasterDirectRleImage(surface, image, task->opacity);
else if (image.scaled) return rasterScaledRleImage(surface, image, task->transform, bbox, task->opacity);
else {
//create a intermediate buffer for rle clipping
auto cmp = request(sizeof(pixel_t), false);
cmp->compositor->method = MaskMethod::None;
cmp->compositor->valid = true;
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);
}
//Whole Image
} else {
if (image.direct) return rasterDirectImage(surface, image, bbox, task->opacity);
else if (image.scaled) return rasterScaledImage(surface, image, task->transform, bbox, task->opacity);
else return rasterTexmapPolygon(surface, image, task->transform, bbox, task->opacity);
}
} }
@ -596,8 +621,7 @@ bool SwRenderer::endComposite(RenderCompositor* cmp)
//Default is alpha blending //Default is alpha blending
if (p->method == MaskMethod::None) { if (p->method == MaskMethod::None) {
auto m = tvg::identity(); return rasterDirectImage(surface, p->image, p->bbox, p->opacity);
return rasterImage(surface, &p->image, m, p->bbox, p->opacity);
} }
return true; return true;

View file

@ -56,6 +56,8 @@ public:
bool sync() override; bool sync() override;
bool target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs); bool target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs);
SwSurface* request(int channelSize, bool square);
RenderCompositor* target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags) override; RenderCompositor* target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags) override;
bool beginComposite(RenderCompositor* cmp, MaskMethod method, uint8_t opacity) override; bool beginComposite(RenderCompositor* cmp, MaskMethod method, uint8_t opacity) override;
bool endComposite(RenderCompositor* cmp) override; bool endComposite(RenderCompositor* cmp) override;
@ -80,7 +82,6 @@ private:
SwRenderer(); SwRenderer();
~SwRenderer(); ~SwRenderer();
SwSurface* request(int channelSize, bool square);
RenderData prepareCommon(SwTask* task, const Matrix& transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags); RenderData prepareCommon(SwTask* task, const Matrix& transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags);
}; };