sw_engine: improve the masking methods

Corrected subtract/intersect masking behaviors that were inaccurately functioning
within chain-masking.

This also helps to enhance the masking performance by reducing the
masking region when applying subtract/intersect.

@Issue: https://github.com/thorvg/thorvg/issues/1606
This commit is contained in:
Hermet Park 2023-08-24 18:23:34 +09:00
parent 7eeba75472
commit 624b275628
9 changed files with 349 additions and 606 deletions

View file

@ -38,6 +38,12 @@ void tvgDrawCmds(tvg::Canvas* canvas)
file.read(reinterpret_cast<char*>(data), sizeof (uint32_t) * 200 * 300);
file.close();
//background
auto bg = tvg::Shape::gen();
bg->appendRect(0, 0, 625, HEIGHT);
bg->fill(50, 50, 50);
canvas->push(move(bg));
{
//Shape + Shape Mask Add
auto shape = tvg::Shape::gen();
@ -54,7 +60,7 @@ void tvgDrawCmds(tvg::Canvas* canvas)
//Shape + Shape Mask Subtract
auto shape2 = tvg::Shape::gen();
shape2->appendCircle(375, 100, 50, 50);
shape2->fill(255, 255, 255);
shape2->fill(255, 255, 255, 127);
auto mask2 = tvg::Shape::gen();
mask2->appendCircle(400, 100, 50, 50);
@ -67,7 +73,7 @@ void tvgDrawCmds(tvg::Canvas* canvas)
//Shape + Shape Mask Intersect
auto shape3 = tvg::Shape::gen();
shape3->appendCircle(625, 100, 50, 50);
shape3->fill(255, 255, 255);
shape3->fill(255, 255, 255, 127);
auto mask3 = tvg::Shape::gen();
mask3->appendCircle(650, 100, 50, 50);
@ -105,7 +111,7 @@ void tvgDrawCmds(tvg::Canvas* canvas)
//Shape + Image Mask Subtract
auto shape2 = tvg::Shape::gen();
shape2->appendCircle(400, 250, 50, 50);
shape2->fill(255, 255, 255);
shape2->fill(255, 255, 255, 127);
auto mask2 = tvg::Picture::gen();
if (mask2->load(data, 200, 300, true) != tvg::Result::Success) return;
@ -118,7 +124,7 @@ void tvgDrawCmds(tvg::Canvas* canvas)
//Shape + Image Mask Intersect
auto shape3 = tvg::Shape::gen();
shape3->appendCircle(650, 250, 50, 50);
shape3->fill(255, 255, 255);
shape3->fill(255, 255, 255, 127);
auto mask3 = tvg::Picture::gen();
if (mask3->load(data, 200, 300, true) != tvg::Result::Success) return;
@ -158,7 +164,7 @@ void tvgDrawCmds(tvg::Canvas* canvas)
//Rect + Rect Mask Subtract
auto shape2 = tvg::Shape::gen();
shape2->appendRect(325, 500, 100, 100);
shape2->fill(255, 255, 255);
shape2->fill(255, 255, 255, 127);
auto mask2 = tvg::Shape::gen();
mask2->appendRect(375, 450, 100, 100);
@ -170,7 +176,7 @@ void tvgDrawCmds(tvg::Canvas* canvas)
//Rect + Rect Mask Intersect
auto shape3 = tvg::Shape::gen();
shape3->appendRect(575, 500, 100, 100);
shape3->fill(255, 255, 255);
shape3->fill(255, 255, 255, 127);
auto mask3 = tvg::Shape::gen();
mask3->appendRect(625, 450, 100, 100);
@ -212,6 +218,7 @@ void tvgDrawCmds(tvg::Canvas* canvas)
image2->translate(400, 650);
image2->scale(0.5f);
image2->rotate(45);
image2->opacity(127);
auto mask2 = tvg::Shape::gen();
mask2->appendCircle(375, 700, 50, 50);
@ -225,6 +232,7 @@ void tvgDrawCmds(tvg::Canvas* canvas)
image3->translate(650, 650);
image3->scale(0.5f);
image3->rotate(45);
image3->opacity(127);
auto mask3 = tvg::Shape::gen();
mask3->appendCircle(625, 700, 50, 50);
@ -278,6 +286,7 @@ void tvgDrawCmds(tvg::Canvas* canvas)
fill2->linear(300, 850, 400, 950);
fill2->colorStops(colorStops, 2);
shape2->fill(std::move(fill2));
shape2->opacity(127);
auto mask2 = tvg::Shape::gen();
mask2->appendCircle(400, 900, 50, 50);
@ -296,6 +305,7 @@ void tvgDrawCmds(tvg::Canvas* canvas)
fill3->linear(550, 850, 650, 950);
fill3->colorStops(colorStops, 2);
shape3->fill(std::move(fill3));
shape3->opacity(127);
auto mask3 = tvg::Shape::gen();
mask3->appendCircle(650, 900, 50, 50);

View file

@ -463,46 +463,6 @@ static inline uint32_t opBlendSoftLight(uint32_t s, uint32_t d, TVG_UNUSED uint8
return JOIN(255, c1, c2, c3);
}
static inline uint32_t opMaskAdd(uint32_t s, uint32_t d, uint8_t a)
{
return opBlendNormal(s, d, a);
}
static inline uint32_t opMaskSubtract(uint32_t s, uint32_t d, uint8_t a)
{
return ALPHA_BLEND(d, MULTIPLY(IA(s), a));
}
static inline uint32_t opMaskDifference(uint32_t s, uint32_t d, uint8_t a)
{
auto t = ALPHA_BLEND(s, a);
return ALPHA_BLEND(t, IA(d)) + ALPHA_BLEND(d, IA(t));
}
static inline uint32_t opMaskIntersect(uint32_t s, uint32_t d, uint8_t a)
{
return ALPHA_BLEND(d, MULTIPLY(IA(s), a));
}
static inline uint32_t opMaskPreAdd(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
{
return opBlendPreNormal(s, d, a);
}
static inline uint32_t opMaskPreSubtract(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
{
return ALPHA_BLEND(d, IA(s));
}
static inline uint32_t opMaskPreDifference(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
{
return ALPHA_BLEND(s, IA(d)) + ALPHA_BLEND(d, IA(s));
}
static inline uint32_t opMaskPreIntersect(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
{
return ALPHA_BLEND(d, MULTIPLY(a, IA(s)));
}
int64_t mathMultiply(int64_t a, int64_t b);
int64_t mathDivide(int64_t a, int64_t b);
@ -554,9 +514,11 @@ void fillFree(SwFill* fill);
//OPTIMIZE_ME: Skip the function pointer access
void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a); //blending ver.
void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a); //blending + BlendingMethod(op2) ver.
void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint32_t* cmp, SwBlender op, uint8_t a); //direct masking ver.
void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity); //masking ver.
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a); //blending ver.
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a); //blending + BlendingMethod(op2) ver.
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint32_t* cmp, SwBlender op, uint8_t a) ; //direct masking ver.
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity); //masking ver.
SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias);

View file

@ -279,6 +279,27 @@ void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
}
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint32_t* cmp, SwBlender op, uint8_t a)
{
auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX;
auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY;
// detSecondDerivative = d(detFirstDerivative)/dx = d( d(det)/dx )/dx
auto detSecondDerivative = fill->radial.detSecDeriv;
// detFirstDerivative = d(det)/dx
auto detFirstDerivative = 2.0f * (fill->radial.a11 * rx + fill->radial.a21 * ry) + 0.5f * detSecondDerivative;
auto det = rx * rx + ry * ry;
for (uint32_t i = 0 ; i < len ; ++i, ++dst, ++cmp) {
auto tmp = op(_pixel(fill, sqrtf(det)), *cmp, a);
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
det += detFirstDerivative;
detFirstDerivative += detSecondDerivative;
}
}
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a)
{
auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX;
@ -423,6 +444,51 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
}
void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint32_t* cmp, SwBlender op, uint8_t a)
{
//Rotation
float rx = x + 0.5f;
float ry = y + 0.5f;
float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
if (mathZero(inc)) {
auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
for (uint32_t i = 0; i < len; ++i, ++dst, ++cmp) {
auto tmp = op(color, *cmp, a);
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
}
return;
}
auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
auto vMin = -vMax;
auto v = t + (inc * len);
//we can use fixed point math
if (v < vMax && v > vMin) {
auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
for (uint32_t j = 0; j < len; ++j, ++dst, ++cmp) {
auto tmp = op(_fixedPixel(fill, t2), *cmp, a);
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
t2 += inc2;
}
//we have to fallback to float math
} else {
uint32_t counter = 0;
while (counter++ < len) {
auto tmp = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *cmp, a);
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
++dst;
++cmp;
t += inc;
}
}
}
void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a)
{
//Rotation

View file

@ -44,6 +44,11 @@ struct FillLinear
fillLinear(fill, dst, y, x, len, op, a);
}
void operator()(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint32_t* cmp, SwBlender op, uint8_t a)
{
fillLinear(fill, dst, y, x, len, cmp, op, a);
}
void operator()(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity)
{
fillLinear(fill, dst, y, x, len, cmp, alpha, csize, opacity);
@ -63,6 +68,11 @@ struct FillRadial
fillRadial(fill, dst, y, x, len, op, a);
}
void operator()(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint32_t* cmp, SwBlender op, uint8_t a)
{
fillRadial(fill, dst, y, x, len, cmp, op, a);
}
void operator()(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity)
{
fillRadial(fill, dst, y, x, len, cmp, alpha, csize, opacity);
@ -162,9 +172,15 @@ static inline uint32_t _opMaskAdd(uint32_t s, uint32_t d, uint8_t a)
}
static inline uint32_t _opMaskSubtract(TVG_UNUSED uint32_t s, uint32_t d, uint8_t a)
static inline uint32_t _opMaskSubtract(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
{
return ALPHA_BLEND(d, a);
return ALPHA_BLEND(s, IA(d));
}
static inline uint32_t _opMaskIntersect(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
{
return ALPHA_BLEND(s, A(d));
}
@ -182,7 +198,7 @@ static inline uint32_t _opAMaskAdd(uint32_t s, uint32_t d, uint8_t a)
static inline uint32_t _opAMaskSubtract(TVG_UNUSED uint32_t s, uint32_t d, uint8_t a)
{
return ALPHA_BLEND(d, IA(ALPHA_BLEND(s, a)));
return ALPHA_BLEND(s, MULTIPLY(IA(d), a));
}
@ -193,12 +209,27 @@ static inline uint32_t _opAMaskDifference(uint32_t s, uint32_t d, uint8_t a)
}
static inline uint32_t _opAMaskIntersect(uint32_t s, uint32_t d, uint8_t a)
{
return ALPHA_BLEND(s, MULTIPLY(A(d), a));
}
static inline bool _direct(CompositeMethod method)
{
//subtract & Intersect allows the direct composition
if (method == CompositeMethod::SubtractMask || method == CompositeMethod::IntersectMask) return true;
return false;
}
static inline SwBlender _getMaskOp(CompositeMethod method)
{
switch (method) {
case CompositeMethod::AddMask: return _opMaskAdd;
case CompositeMethod::SubtractMask: return _opMaskSubtract;
case CompositeMethod::DifferenceMask: return _opMaskDifference;
case CompositeMethod::IntersectMask: return _opMaskIntersect;
default: return nullptr;
}
}
@ -210,6 +241,7 @@ static inline SwBlender _getAMaskOp(CompositeMethod method)
case CompositeMethod::AddMask: return _opAMaskAdd;
case CompositeMethod::SubtractMask: return _opAMaskSubtract;
case CompositeMethod::DifferenceMask: return _opAMaskDifference;
case CompositeMethod::IntersectMask: return _opAMaskIntersect;
default: return nullptr;
}
}
@ -284,7 +316,7 @@ static uint32_t _interpDownScaler(const uint32_t *img, uint32_t stride, uint32_t
/* Rect */
/************************************************************************/
static void _rasterMaskedRectDup(SwSurface* surface, const SwBBox& region, SwBlender opMask, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
static bool _rasterCompositeMaskedRect(SwSurface* surface, const SwBBox& region, SwBlender maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
@ -296,45 +328,33 @@ static void _rasterMaskedRectDup(SwSurface* surface, const SwBBox& region, SwBle
for (uint32_t y = 0; y < h; ++y) {
auto cmp = cbuffer;
for (uint32_t x = 0; x < w; ++x, ++cmp) {
*cmp = opMask(color, *cmp, ialpha);
*cmp = maskOp(color, *cmp, ialpha);
}
cbuffer += cstride;
}
return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
static void _rasterMaskedRectInt(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
static bool _rasterDirectMaskedRect(SwSurface* surface, const SwBBox& region, SwBlender maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto cstride = surface->compositor->image.stride;
auto cbuffer = surface->compositor->image.buf32 + (region.min.y * surface->compositor->image.stride + region.min.x); //compositor buffer
auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x); //destination buffer
auto color = surface->join(r, g, b, a);
for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) {
auto cmp = surface->compositor->image.buf32 + (y * cstride + surface->compositor->bbox.min.x);
if (y == region.min.y) {
for (auto y2 = y; y2 < region.max.y; ++y2) {
auto tmp = cmp;
auto x = surface->compositor->bbox.min.x;
while (x < surface->compositor->bbox.max.x) {
if (x == region.min.x) {
for (uint32_t i = 0; i < w; ++i, ++tmp) {
*tmp = ALPHA_BLEND(*tmp, a);
}
x += w;
} else {
*tmp = 0;
++tmp;
++x;
}
}
cmp += cstride;
}
y += (h - 1);
} else {
rasterPixel32(cmp, 0x00000000, 0, w);
cmp += cstride;
for (uint32_t y = 0; y < h; ++y) {
auto cmp = cbuffer;
auto dst = dbuffer;
for (uint32_t x = 0; x < w; ++x, ++cmp, ++dst) {
auto tmp = maskOp(color, *cmp, 0); //not use alpha.
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
}
cbuffer += surface->compositor->image.stride;
dbuffer += surface->stride;
}
return true;
}
@ -343,21 +363,15 @@ static bool _rasterMaskedRect(SwSurface* surface, const SwBBox& region, uint8_t
//32bit channels composition
if (surface->channelSize != sizeof(uint32_t)) return false;
TVGLOG("SW_ENGINE", "Masked(%d) Rect [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.max.y, region.min.y);
TVGLOG("SW_ENGINE", "Masked(%d) Rect [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
if (surface->compositor->method == CompositeMethod::IntersectMask) {
_rasterMaskedRectInt(surface, region, r, g, b, a);
} else if (auto opMask = _getMaskOp(surface->compositor->method)) {
//Other Masking operations: Add, Subtract, Difference ...
_rasterMaskedRectDup(surface, region, opMask, r, g, b, a);
} else {
auto maskOp = _getMaskOp(surface->compositor->method);
if (_direct(surface->compositor->method)) return _rasterDirectMaskedRect(surface, region, maskOp, r, g, b, a);
else return _rasterCompositeMaskedRect(surface, region, maskOp, r, g, b, a);
return false;
}
//Masking Composition
return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
static bool _rasterMattedRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
@ -471,7 +485,7 @@ static bool _rasterRect(SwSurface* surface, const SwBBox& region, uint8_t r, uin
/* Rle */
/************************************************************************/
static void _rasterMaskedRleDup(SwSurface* surface, SwRleData* rle, SwBlender maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRleData* rle, SwBlender maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
auto span = rle->spans;
auto cbuffer = surface->compositor->image.buf32;
@ -488,10 +502,11 @@ static void _rasterMaskedRleDup(SwSurface* surface, SwRleData* rle, SwBlender ma
*cmp = maskOp(src, *cmp, ialpha);
}
}
return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
static void _rasterMaskedRleInt(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
static bool _rasterDirectMaskedRle(SwSurface* surface, SwRleData* rle, SwBlender maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
auto span = rle->spans;
auto cbuffer = surface->compositor->image.buf32;
@ -499,25 +514,17 @@ static void _rasterMaskedRleInt(SwSurface* surface, SwRleData* rle, uint8_t r, u
auto color = surface->join(r, g, b, a);
uint32_t src;
for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) {
auto cmp = &cbuffer[y * cstride];
auto x = surface->compositor->bbox.min.x;
while (x < surface->compositor->bbox.max.x) {
if (y == span->y && x == span->x && x + span->len <= surface->compositor->bbox.max.x) {
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
auto cmp = &cbuffer[span->y * cstride + span->x];
auto dst = &surface->buf32[span->y * surface->stride + span->x];
if (span->coverage == 255) src = color;
else src = ALPHA_BLEND(color, span->coverage);
auto alpha = A(src);
for (uint32_t i = 0; i < span->len; ++i) {
cmp[x + i] = ALPHA_BLEND(cmp[x + i], alpha);
}
x += span->len;
++span;
} else {
cmp[x] = 0;
++x;
}
for (auto x = 0; x < span->len; ++x, ++cmp, ++dst) {
auto tmp = maskOp(src, *cmp, 0); //not use alpha
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
}
}
return true;
}
@ -528,17 +535,11 @@ static bool _rasterMaskedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint
//32bit channels composition
if (surface->channelSize != sizeof(uint32_t)) return false;
if (surface->compositor->method == CompositeMethod::IntersectMask) {
_rasterMaskedRleInt(surface, rle, r, g, b, a);
} else if (auto opMask = _getMaskOp(surface->compositor->method)) {
//Other Masking operations: Add, Subtract, Difference ...
_rasterMaskedRleDup(surface, rle, opMask, r, g, b, a);
} else {
return false;
}
auto maskOp = _getMaskOp(surface->compositor->method);
//Masking Composition
return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
if (_direct(surface->compositor->method)) return _rasterDirectMaskedRle(surface, rle, maskOp, r, g, b, a);
else return _rasterCompositeMaskedRle(surface, rle, maskOp, r, g, b, a);
return false;
}
@ -668,29 +669,11 @@ static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g,
}
/************************************************************************/
/* RLE Transformed Image */
/************************************************************************/
static bool _transformedRleImage(SwSurface* surface, const SwImage* image, const Matrix* transform, uint8_t opacity)
{
auto ret = _rasterTexmapPolygon(surface, image, transform, nullptr, opacity);
//Masking Composition
if (_compositing(surface) && _masking(surface)) {
return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
return ret;
}
/************************************************************************/
/* RLE Scaled Image */
/************************************************************************/
static void _rasterScaledMaskedRleImageDup(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwBlender maskOp, SwBlender amaskOp, uint8_t opacity)
static bool _rasterCompositeScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwBlender maskOp, SwBlender amaskOp, uint8_t opacity)
{
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
@ -718,47 +701,42 @@ static void _rasterScaledMaskedRleImageDup(SwSurface* surface, const SwImage* im
}
}
}
return true;
}
static void _rasterScaledMaskedRleImageInt(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
static bool _rasterDirectScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwBlender maskOp, SwBlender amaskOp, uint8_t opacity)
{
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto sampleSize2 = sampleSize * sampleSize;
auto span = image->rle->spans;
auto cbuffer = surface->compositor->image.buf32;
auto cstride = surface->compositor->image.stride;
for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) {
auto cmp = &cbuffer[y * cstride];
for (auto x = surface->compositor->bbox.min.x; x < surface->compositor->bbox.max.x; ++x) {
if (y == span->y && x == span->x && x + span->len <= surface->compositor->bbox.max.x) {
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
auto sy = span->y * itransform->e22 + itransform->e23;
if ((uint32_t)sy >= image->h) continue;
auto alpha = MULTIPLY(span->coverage, opacity);
if (alpha == 255) {
for (uint32_t i = 0; i < span->len; ++i) {
auto sx = (x + i) * itransform->e11 + itransform->e13;
auto cmp = &surface->compositor->image.buf32[span->y * surface->compositor->image.stride + span->x];
auto dst = &surface->buf32[span->y * surface->stride + span->x];
auto a = MULTIPLY(span->coverage, opacity);
if (a == 255) {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp, ++dst) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
cmp[x + i] = ALPHA_BLEND(cmp[x + i], A(src));
auto tmp = maskOp(src, *cmp, 0); //not use alpha
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
}
} else {
for (uint32_t i = 0; i < span->len; ++i) {
auto sx = (x + i) * itransform->e11 + itransform->e13;
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp, ++dst) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
cmp[x + i] = ALPHA_BLEND(cmp[x + i], A(ALPHA_BLEND(src, alpha)));
}
}
x += span->len - 1;
++span;
} else {
cmp[x] = 0;
auto tmp = amaskOp(src, *cmp, a);
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
}
}
}
return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
@ -766,17 +744,13 @@ static bool _rasterScaledMaskedRleImage(SwSurface* surface, const SwImage* image
{
TVGLOG("SW_ENGINE", "Scaled Masked(%d) Rle Image", (int)surface->compositor->method);
if (surface->compositor->method == CompositeMethod::IntersectMask) {
_rasterScaledMaskedRleImageInt(surface, image, itransform, region, opacity);
} else if (auto opMask = _getMaskOp(surface->compositor->method)) {
//Other Masking operations: Add, Subtract, Difference ...
_rasterScaledMaskedRleImageDup(surface, image, itransform, region, opMask, _getAMaskOp(surface->compositor->method), opacity);
} else {
auto maskOp = _getMaskOp(surface->compositor->method);
auto amaskOp = _getAMaskOp(surface->compositor->method);
if (_direct(surface->compositor->method)) return _rasterDirectScaledMaskedRleImage(surface, image, itransform, region, maskOp, amaskOp, opacity);
else return _rasterCompositeScaledMaskedRleImage(surface, image, itransform, region, maskOp, amaskOp, opacity);
return false;
}
//Masking Composition
return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
static bool _rasterScaledMattedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
@ -917,7 +891,7 @@ static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matr
/* RLE Direct Image */
/************************************************************************/
static void _rasterDirectMaskedRleImageDup(SwSurface* surface, const SwImage* image, SwBlender maskOp, SwBlender amaskOp, uint8_t opacity)
static bool _rasterCompositeDirectMaskedRleImage(SwSurface* surface, const SwImage* image, SwBlender maskOp, SwBlender amaskOp, uint8_t opacity)
{
auto span = image->rle->spans;
auto cbuffer = surface->compositor->image.buf32;
@ -937,40 +911,34 @@ static void _rasterDirectMaskedRleImageDup(SwSurface* surface, const SwImage* im
}
}
}
return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
static void _rasterDirectMaskedRleImageInt(SwSurface* surface, const SwImage* image, uint8_t opacity)
static bool _rasterDirectDirectMaskedRleImage(SwSurface* surface, const SwImage* image, SwBlender maskOp, SwBlender amaskOp, uint8_t opacity)
{
auto span = image->rle->spans;
auto cbuffer = surface->compositor->image.buf32;
auto ctride = surface->compositor->image.stride;
for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) {
auto cmp = &cbuffer[y * ctride];
auto x = surface->compositor->bbox.min.x;
while (x < surface->compositor->bbox.max.x) {
if (y == span->y && x == span->x && x + span->len <= surface->compositor->bbox.max.x) {
auto alpha = MULTIPLY(span->coverage, opacity);
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
auto src = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox);
auto cmp = &cbuffer[span->y * ctride + span->x];
auto dst = &surface->buf32[span->y * surface->stride + span->x];
auto alpha = MULTIPLY(span->coverage, opacity);
if (alpha == 255) {
for (uint32_t i = 0; i < span->len; ++i, ++src) {
cmp[x + i] = ALPHA_BLEND(cmp[x + i], A(*src));
for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp, ++dst) {
auto tmp = maskOp(*src, *cmp, 0); //not use alpha
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
}
} else {
for (uint32_t i = 0; i < span->len; ++i, ++src) {
auto t = ALPHA_BLEND(*src, alpha);
cmp[x + i] = ALPHA_BLEND(cmp[x + i], A(t));
}
}
x += span->len;
++span;
} else {
cmp[x] = 0;
++x;
for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp, ++dst) {
auto tmp = amaskOp(*src, *cmp, alpha);
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
}
}
}
return true;
}
@ -978,17 +946,12 @@ static bool _rasterDirectMaskedRleImage(SwSurface* surface, const SwImage* image
{
TVGLOG("SW_ENGINE", "Direct Masked(%d) Rle Image", (int)surface->compositor->method);
if (surface->compositor->method == CompositeMethod::IntersectMask) {
_rasterDirectMaskedRleImageInt(surface, image, opacity);
} else if (auto opMask = _getMaskOp(surface->compositor->method)) {
//Other Masking operations: Add, Subtract, Difference ...
_rasterDirectMaskedRleImageDup(surface, image, opMask, _getAMaskOp(surface->compositor->method), opacity);
} else {
return false;
}
auto maskOp = _getMaskOp(surface->compositor->method);
auto amaskOp = _getAMaskOp(surface->compositor->method);
//Masking Composition
return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
if (_direct(surface->compositor->method)) _rasterDirectDirectMaskedRleImage(surface, image, maskOp, amaskOp, opacity);
else return _rasterCompositeDirectMaskedRleImage(surface, image, maskOp, amaskOp, opacity);
return false;
}
@ -1088,35 +1051,11 @@ static bool _directRleImage(SwSurface* surface, const SwImage* image, uint8_t op
}
/************************************************************************/
/* Transformed Image */
/************************************************************************/
static bool _transformedImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint8_t opacity)
{
auto ret = _rasterTexmapPolygon(surface, image, transform, &region, opacity);
//Masking Composition
if (_compositing(surface) && _masking(surface)) {
return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
return ret;
}
static bool _transformedImageMesh(SwSurface* surface, const SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox* region, uint8_t opacity)
{
//TODO: Not completed for all cases.
return _rasterTexmapPolygonMesh(surface, image, mesh, transform, region, opacity);
}
/************************************************************************/
/*Scaled Image */
/************************************************************************/
static void _rasterScaledMaskedImageDup(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwBlender maskOp, SwBlender amaskOp, uint8_t opacity)
static bool _rasterCompositeScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwBlender maskOp, SwBlender amaskOp, uint8_t opacity)
{
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
@ -1145,61 +1084,45 @@ static void _rasterScaledMaskedImageDup(SwSurface* surface, const SwImage* image
}
cbuffer += cstride;
}
return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
static void _rasterScaledMaskedImageInt(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
static bool _rasterDirectScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwBlender maskOp, SwBlender amaskOp, uint8_t opacity)
{
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto sampleSize2 = sampleSize * sampleSize;
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto cstride = surface->compositor->image.stride;
auto cbuffer = surface->compositor->image.buf32 + (surface->compositor->bbox.min.y * cstride + surface->compositor->bbox.min.x);
auto cbuffer = surface->compositor->image.buf32 + (region.min.y * cstride + region.min.x);
auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x);
for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) {
if (y == region.min.y) {
auto cbuffer2 = cbuffer;
for (auto y2 = y; y2 < region.max.y; ++y2) {
auto sy = y2 * itransform->e22 + itransform->e23;
for (auto y = region.min.y; y < region.max.y; ++y) {
auto sy = y * itransform->e22 + itransform->e23;
if ((uint32_t)sy >= image->h) continue;
auto tmp = cbuffer2;
auto x = surface->compositor->bbox.min.x;
while (x < surface->compositor->bbox.max.x) {
if (x == region.min.x) {
auto cmp = cbuffer;
auto dst = dbuffer;
if (opacity == 255) {
for (uint32_t i = 0; i < w; ++i, ++tmp) {
auto sx = (x + i) * itransform->e11 + itransform->e13;
for (auto x = region.min.x; x < region.max.x; ++x, ++cmp, ++dst) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
*tmp = ALPHA_BLEND(*tmp, A(src));
auto tmp = maskOp(src, *cmp, 0); //not use alpha
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
}
} else {
for (uint32_t i = 0; i < w; ++i, ++tmp) {
auto sx = (x + i) * itransform->e11 + itransform->e13;
for (auto x = region.min.x; x < region.max.x; ++x, ++cmp, ++dst) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
auto src = ALPHA_BLEND(scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2), opacity);
*tmp = ALPHA_BLEND(*tmp, A(src));
}
}
x += w;
} else {
*tmp = 0;
++tmp;
++x;
}
}
cbuffer2 += cstride;
}
y += (h - 1);
} else {
auto tmp = cbuffer;
for (auto x = surface->compositor->bbox.min.x; x < surface->compositor->bbox.max.x; ++x, ++tmp) {
*tmp = 0;
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
auto tmp = amaskOp(src, *cmp, opacity);
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
}
}
cbuffer += cstride;
dbuffer += surface->stride;
}
return true;
}
@ -1207,17 +1130,12 @@ static bool _rasterScaledMaskedImage(SwSurface* surface, const SwImage* image, c
{
TVGLOG("SW_ENGINE", "Scaled Masked(%d) Image [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
if (surface->compositor->method == CompositeMethod::IntersectMask) {
_rasterScaledMaskedImageInt(surface, image, itransform, region, opacity);
} else if (auto opMask = _getMaskOp(surface->compositor->method)) {
//Other Masking operations: Add, Subtract, Difference ...
_rasterScaledMaskedImageDup(surface, image, itransform, region, opMask, _getAMaskOp(surface->compositor->method), opacity);
} else {
return false;
}
auto maskOp = _getMaskOp(surface->compositor->method);
auto amaskOp = _getAMaskOp(surface->compositor->method);
//Masking Composition
return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
if (_direct(surface->compositor->method)) return _rasterDirectScaledMaskedImage(surface, image, itransform, region, maskOp, amaskOp, opacity);
else return _rasterCompositeScaledMaskedImage(surface, image, itransform, region, maskOp, amaskOp, opacity);
return false;
}
@ -1351,7 +1269,7 @@ static bool _scaledImage(SwSurface* surface, const SwImage* image, const Matrix*
/* Direct Image */
/************************************************************************/
static void _rasterDirectMaskedImageDup(SwSurface* surface, const SwImage* image, const SwBBox& region, SwBlender maskOp, SwBlender amaskOp, uint8_t opacity)
static bool _rasterCompositeDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, SwBlender maskOp, SwBlender amaskOp, uint8_t opacity)
{
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
@ -1375,50 +1293,40 @@ static void _rasterDirectMaskedImageDup(SwSurface* surface, const SwImage* image
cbuffer += cstride;
sbuffer += image->stride;
}
return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
static void _rasterDirectMaskedImageInt(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity)
static bool _rasterDirectDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, SwBlender maskOp, SwBlender amaskOp, uint8_t opacity)
{
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto cstride = surface->compositor->image.stride;
auto cbuffer = surface->compositor->image.buf32 + (surface->compositor->bbox.min.y * cstride + surface->compositor->bbox.min.x);
for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) {
if (y == region.min.y) {
auto cbuffer2 = cbuffer;
for (auto y2 = y; y2 < region.max.y; ++y2) {
auto tmp = cbuffer2;
auto x = surface->compositor->bbox.min.x;
while (x < surface->compositor->bbox.max.x) {
if (x == region.min.x) {
auto src = &image->buf32[(y2 + image->oy) * image->stride + (x + image->ox)];
auto cbuffer = surface->compositor->image.buf32 + (region.min.y * cstride + region.min.x); //compositor buffer
auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x); //destination buffer
auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
for (uint32_t y = 0; y < h; ++y) {
auto cmp = cbuffer;
auto dst = dbuffer;
auto src = sbuffer;
if (opacity == 255) {
for (uint32_t i = 0; i < w; ++i, ++tmp, ++src) {
*tmp = ALPHA_BLEND(*tmp, A(*src));
for (uint32_t x = 0; x < w; ++x, ++src, ++cmp, ++dst) {
auto tmp = maskOp(*src, *cmp, 0); //not use alpha
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
}
} else {
for (uint32_t i = 0; i < w; ++i, ++tmp, ++src) {
auto t = ALPHA_BLEND(*src, opacity);
*tmp = ALPHA_BLEND(*tmp, A(t));
for (uint32_t x = 0; x < w; ++x, ++src, ++cmp, ++dst) {
auto tmp = amaskOp(*src, *cmp, opacity);
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
}
}
x += w;
} else {
*tmp = 0;
++tmp;
++x;
}
}
cbuffer2 += cstride;
}
y += (h - 1);
} else {
rasterPixel32(cbuffer, 0x00000000, 0, surface->compositor->bbox.max.x - surface->compositor->bbox.min.x);
}
cbuffer += cstride;
dbuffer += surface->stride;
sbuffer += image->stride;
}
return true;
}
@ -1426,17 +1334,13 @@ static bool _rasterDirectMaskedImage(SwSurface* surface, const SwImage* image, c
{
TVGLOG("SW_ENGINE", "Direct Masked(%d) Image [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
if (surface->compositor->method == CompositeMethod::IntersectMask) {
_rasterDirectMaskedImageInt(surface, image, region, opacity);
} else if (auto opMask = _getMaskOp(surface->compositor->method)) {
//Other Masking operations: Add, Subtract, Difference ...
_rasterDirectMaskedImageDup(surface, image, region, opMask, _getAMaskOp(surface->compositor->method), opacity);
} else {
auto maskOp = _getMaskOp(surface->compositor->method);
auto amaskOp = _getAMaskOp(surface->compositor->method);
if (_direct(surface->compositor->method)) return _rasterDirectDirectMaskedImage(surface, image, region, maskOp, amaskOp, opacity);
else return _rasterCompositeDirectMaskedImage(surface, image, region, maskOp, amaskOp, opacity);
return false;
}
//Masking Composition
return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity)
@ -1549,12 +1453,12 @@ static bool _rasterImage(SwSurface* surface, SwImage* image, const Matrix* trans
if (image->rle) {
if (image->direct) return _directRleImage(surface, image, opacity);
else if (image->scaled) return _scaledRleImage(surface, image, transform, region, opacity);
else return _transformedRleImage(surface, image, transform, opacity);
else return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity);
//Whole Image
} else {
if (image->direct) return _directImage(surface, image, region, opacity);
else if (image->scaled) return _scaledImage(surface, image, transform, region, opacity);
else return _transformedImage(surface, image, transform, region, opacity);
else return _rasterTexmapPolygon(surface, image, transform, &region, opacity);
}
}
@ -1564,7 +1468,7 @@ static bool _rasterImage(SwSurface* surface, SwImage* image, const Matrix* trans
/************************************************************************/
template<typename fillMethod>
static void _rasterGradientMaskedRectDup(SwSurface* surface, const SwBBox& region, const SwFill* fill, SwBlender maskOp)
static bool _rasterCompositeGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill, SwBlender maskOp)
{
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
@ -1575,41 +1479,25 @@ static void _rasterGradientMaskedRectDup(SwSurface* surface, const SwBBox& regio
fillMethod()(fill, cbuffer, region.min.y + y, region.min.x, w, maskOp, 255);
cbuffer += surface->stride;
}
return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox, 255);
}
template<typename fillMethod>
static void _rasterGradientMaskedRectInt(SwSurface* surface, const SwBBox& region, const SwFill* fill)
static bool _rasterDirectGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill, SwBlender maskOp)
{
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto cstride = surface->compositor->image.stride;
auto cbuffer = surface->compositor->image.buf32 + (region.min.y * cstride + region.min.x);
auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x);
for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) {
auto cmp = surface->compositor->image.buf32 + (y * cstride + surface->compositor->bbox.min.x);
if (y == region.min.y) {
for (auto y2 = y; y2 < region.max.y; ++y2) {
auto tmp = cmp;
auto x = surface->compositor->bbox.min.x;
while (x < surface->compositor->bbox.max.x) {
if (x == region.min.x) {
fillMethod()(fill, tmp, y2, x, w, opMaskPreIntersect, 255);
x += w;
tmp += w;
} else {
*tmp = 0;
++tmp;
++x;
}
}
cmp += cstride;
}
y += (h - 1);
} else {
rasterPixel32(cmp, 0x00000000, 0, surface->compositor->bbox.max.x -surface->compositor->bbox.min.x);
cmp += cstride;
}
for (uint32_t y = 0; y < h; ++y) {
fillMethod()(fill, dbuffer, region.min.y + y, region.min.x, w, cbuffer, maskOp, 255);
cbuffer += cstride;
dbuffer += surface->stride;
}
return true;
}
@ -1618,16 +1506,13 @@ static bool _rasterGradientMaskedRect(SwSurface* surface, const SwBBox& region,
{
auto method = surface->compositor->method;
TVGLOG("SW_ENGINE", "Masked(%d) Gradient [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
TVGLOG("SW_ENGINE", "Masked(%d) Gradient [Region: %lu %lu %lu %lu]", (int)method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
if (method == CompositeMethod::AddMask) _rasterGradientMaskedRectDup<fillMethod>(surface, region, fill, opMaskPreAdd);
else if (method == CompositeMethod::SubtractMask) _rasterGradientMaskedRectDup<fillMethod>(surface, region, fill, opMaskPreSubtract);
else if (method == CompositeMethod::DifferenceMask) _rasterGradientMaskedRectDup<fillMethod>(surface, region, fill, opMaskPreDifference);
else if (method == CompositeMethod::IntersectMask) _rasterGradientMaskedRectInt<fillMethod>(surface, region, fill);
else return false;
auto amaskOp = _getAMaskOp(method);
//Masking Composition
return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox, 255);
if (_direct(method)) return _rasterDirectGradientMaskedRect<fillMethod>(surface, region, fill, amaskOp);
else return _rasterCompositeGradientMaskedRect<fillMethod>(surface, region, fill, amaskOp);
return false;
}
@ -1740,7 +1625,7 @@ static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region,
/************************************************************************/
template<typename fillMethod>
static void _rasterGradientMaskedRleDup(SwSurface* surface, const SwRleData* rle, const SwFill* fill, SwBlender maskOp)
static bool _rasterCompositeGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, SwBlender maskOp)
{
auto span = rle->spans;
auto cstride = surface->compositor->image.stride;
@ -1750,48 +1635,39 @@ static void _rasterGradientMaskedRleDup(SwSurface* surface, const SwRleData* rle
auto cmp = &cbuffer[span->y * cstride + span->x];
fillMethod()(fill, cmp, span->y, span->x, span->len, maskOp, span->coverage);
}
return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox, 255);
}
template<typename fillMethod>
static void _rasterGradientMaskedRleInt(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
static bool _rasterDirectGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, SwBlender maskOp)
{
auto span = rle->spans;
auto cstride = surface->compositor->image.stride;
auto cbuffer = surface->compositor->image.buf32;
auto dbuffer = surface->buf32;
for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) {
auto cmp = &cbuffer[y * cstride];
auto x = surface->compositor->bbox.min.x;
while (x < surface->compositor->bbox.max.x) {
if (y == span->y && x == span->x && x + span->len <= surface->compositor->bbox.max.x) {
fillMethod()(fill, cmp, span->y, span->x, span->len, opMaskIntersect, span->coverage);
x += span->len;
++span;
} else {
cmp[x] = 0;
++x;
}
}
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
auto cmp = &cbuffer[span->y * cstride + span->x];
auto dst = &dbuffer[span->y * surface->stride + span->x];
fillMethod()(fill, dst, span->y, span->x, span->len, cmp, maskOp, span->coverage);
}
return true;
}
template<typename fillMethod>
static bool _rasterGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
{
TVGLOG("SW_ENGINE", "Masked(%d) Rle Linear Gradient", (int)surface->compositor->method);
auto method = surface->compositor->method;
if (method == CompositeMethod::AddMask) _rasterGradientMaskedRleDup<fillMethod>(surface, rle, fill, opMaskAdd);
else if (method == CompositeMethod::SubtractMask) _rasterGradientMaskedRleDup<fillMethod>(surface, rle, fill, opMaskSubtract);
else if (method == CompositeMethod::DifferenceMask) _rasterGradientMaskedRleDup<fillMethod>(surface, rle, fill, opMaskDifference);
else if (method == CompositeMethod::IntersectMask) _rasterGradientMaskedRleInt<fillMethod>(surface, rle, fill);
else return false;
TVGLOG("SW_ENGINE", "Masked(%d) Rle Linear Gradient", (int)method);
//Masking Composition
return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox, 255);
auto amaskOp = _getAMaskOp(method);
if (_direct(method)) return _rasterDirectGradientMaskedRle<fillMethod>(surface, rle, fill, amaskOp);
else return _rasterCompositeGradientMaskedRle<fillMethod>(surface, rle, fill, amaskOp);
return false;
}
@ -2059,7 +1935,6 @@ bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8
g = MULTIPLY(g, a);
b = MULTIPLY(b, a);
}
if (shape->fastTrack) return _rasterRect(surface, shape->bbox, r, g, b, a);
else return _rasterRle(surface, shape->rle, r, g, b, a);
}
@ -2091,7 +1966,7 @@ bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, con
//TODO: case: _rasterRGBImageMesh()
//TODO: case: _rasterGrayscaleImageMesh()
//TODO: case: _rasterAlphaImageMesh()
if (mesh && mesh->triangleCnt > 0) return _transformedImageMesh(surface, image, mesh, transform, &bbox, opacity);
if (mesh && mesh->triangleCnt > 0) return _rasterTexmapPolygonMesh(surface, image, mesh, transform, &bbox, opacity);
else return _rasterImage(surface, image, transform, bbox, opacity);
}

View file

@ -70,186 +70,11 @@ static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, in
}
static void _rasterMaskedPolygonImageSegmentInt(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag)
{
float _dudx = dudx, _dvdx = dvdx;
float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
float _xa = xa, _xb = xb, _ua = ua, _va = va;
auto sbuf = image->buf32;
int32_t sw = static_cast<int32_t>(image->stride);
int32_t sh = image->h;
int32_t x1, x2, ar, ab, iru, irv, px, ay;
int32_t vv = 0, uu = 0;
int32_t minx = INT32_MAX, maxx = INT32_MIN;
float dx, u, v, iptr;
auto cbuffer = surface->compositor->image.buf32;
SwSpan* span = nullptr; //used only when rle based.
if (!_arrange(image, region, yStart, yEnd)) return;
//Clear out of the Polygon vertical ranges
auto size = surface->compositor->bbox.max.x - surface->compositor->bbox.min.x;
if (dirFlag == 1) { //left top case.
for(int y = surface->compositor->bbox.min.y; y < yStart; ++y) {
rasterPixel32(surface->compositor->image.buf32 + y * surface->compositor->image.stride, 0, surface->compositor->bbox.min.x, size);
}
}
if (dirFlag == 4) { //right bottom case.
for(int y = yEnd; y < surface->compositor->bbox.max.y; ++y) {
rasterPixel32(surface->compositor->image.buf32 + y * surface->compositor->image.stride, 0, surface->compositor->bbox.min.x, size);
}
}
//Loop through all lines in the segment
uint32_t spanIdx = 0;
if (region) {
minx = region->min.x;
maxx = region->max.x;
} else {
span = image->rle->spans;
while (span->y < yStart) {
++span;
++spanIdx;
}
}
for (int32_t y = yStart; y < yEnd; ++y) {
auto cmp = &cbuffer[y * surface->compositor->image.stride];
x1 = (int32_t)_xa;
x2 = (int32_t)_xb;
if (!region) {
minx = INT32_MAX;
maxx = INT32_MIN;
//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
//FIXME: this aa must be applied before masking op
ay = y - aaSpans->yStart;
if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
//Range allowed
if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) {
for (int32_t x = surface->compositor->bbox.min.x; x < surface->compositor->bbox.max.x; ++x) {
//Range allowed
if (x >= x1 && x < x2) {
//Perform subtexel pre-stepping on UV
dx = 1 - (_xa - x1);
u = _ua + dx * _dudx;
v = _va + dx * _dvdx;
if ((uint32_t)v >= image->h) {
cmp[x] = 0;
} else {
if (opacity == 255) {
uu = (int) u;
if (uu >= sw) continue;
vv = (int) v;
if (vv >= sh) continue;
ar = (int)(255 * (1 - modff(u, &iptr)));
ab = (int)(255 * (1 - modff(v, &iptr)));
iru = uu + 1;
irv = vv + 1;
px = *(sbuf + (vv * sw) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* right pixel */
int px2 = *(sbuf + (vv * sw) + iru);
px = INTERPOLATE(px, px2, ar);
}
/* vertical interpolate */
if (irv < sh) {
/* bottom pixel */
int px2 = *(sbuf + (irv * sw) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* bottom right pixel */
int px3 = *(sbuf + (irv * sw) + iru);
px2 = INTERPOLATE(px2, px3, ar);
}
px = INTERPOLATE(px, px2, ab);
}
cmp[x] = ALPHA_BLEND(cmp[x], A(px));
//Step UV horizontally
u += _dudx;
v += _dvdx;
} else {
uu = (int) u;
if (uu >= sw) continue;
vv = (int) v;
if (vv >= sh) continue;
ar = (int)(255 * (1 - modff(u, &iptr)));
ab = (int)(255 * (1 - modff(v, &iptr)));
iru = uu + 1;
irv = vv + 1;
px = *(sbuf + (vv * sw) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* right pixel */
int px2 = *(sbuf + (vv * sw) + iru);
px = INTERPOLATE(px, px2, ar);
}
/* vertical interpolate */
if (irv < sh) {
/* bottom pixel */
int px2 = *(sbuf + (irv * sw) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* bottom right pixel */
int px3 = *(sbuf + (irv * sw) + iru);
px2 = INTERPOLATE(px2, px3, ar);
}
px = INTERPOLATE(px, px2, ab);
}
cmp[x] = ALPHA_BLEND(cmp[x], MULTIPLY(A(px), opacity));
//Step UV horizontally
u += _dudx;
v += _dvdx;
}
}
} else {
//Clear out of polygon horizontal range
if (x < x1 && (dirFlag == 1 || dirFlag == 2)) cmp[x] = 0;
else if (x >= x2 && (dirFlag == 3 || dirFlag == 4)) cmp[x] = 0;
}
}
}
//Step along both edges
_xa += _dxdya;
_xb += _dxdyb;
_ua += _dudya;
_va += _dvdya;
}
xa = _xa;
xb = _xb;
ua = _ua;
va = _va;
}
static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImage* image, const SwBBox* region, SwBlender maskOp, SwBlender amaskOp, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity)
static bool _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0)
{
auto maskOp = _getMaskOp(surface->compositor->method);
auto amaskOp = _getAMaskOp(surface->compositor->method);
auto direct = _direct(surface->compositor->method);
float _dudx = dudx, _dvdx = dvdx;
float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
float _xa = xa, _xb = xb, _ua = ua, _va = va;
@ -262,7 +87,7 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
float dx, u, v, iptr;
SwSpan* span = nullptr; //used only when rle based.
if (!_arrange(image, region, yStart, yEnd)) return;
if (!_arrange(image, region, yStart, yEnd)) return false;
//Loop through all lines in the segment
uint32_t spanIdx = 0;
@ -314,6 +139,7 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
x = x1;
auto cmp = &surface->compositor->image.buf32[y * surface->compositor->image.stride + x1];
auto dst = &surface->buf32[y * surface->stride + x1];
if (opacity == 255) {
//Draw horizontal line
@ -349,7 +175,13 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
}
px = INTERPOLATE(px, px2, ab);
}
if (direct) {
auto tmp = maskOp(px, *cmp, 0); //not use alpha
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
++dst;
} else {
*cmp = maskOp(px, *cmp, IA(px));
}
++cmp;
//Step UV horizontally
@ -392,7 +224,14 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
}
px = INTERPOLATE(px, px2, ab);
}
if (direct) {
auto tmp = amaskOp(px, *cmp, opacity);
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
++dst;
} else {
*cmp = amaskOp(px, *cmp, opacity);
}
++cmp;
//Step UV horizontally
@ -418,17 +257,8 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
xb = _xb;
ua = _ua;
va = _va;
}
static void _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0)
{
if (surface->compositor->method == CompositeMethod::IntersectMask) {
_rasterMaskedPolygonImageSegmentInt(surface, image, region, yStart, yEnd, aaSpans, opacity, dirFlag);
} else if (auto opMask = _getMaskOp(surface->compositor->method)) {
//Other Masking operations: Add, Subtract, Difference ...
_rasterMaskedPolygonImageSegmentDup(surface, image, region, opMask, _getAMaskOp(surface->compositor->method), yStart, yEnd, aaSpans, opacity);
}
return true;
}
@ -1294,6 +1124,9 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
_rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity);
if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) {
_rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
return _apply(surface, aaSpans);
}
@ -1342,15 +1175,15 @@ static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, c
}
// Get AA spans and step polygons again to draw
auto aaSpans = _AASpans(ys, ye, image, region);
if (aaSpans) {
if (auto aaSpans = _AASpans(ys, ye, image, region)) {
for (uint32_t i = 0; i < mesh->triangleCnt; i++) {
_rasterPolygonImage(surface, image, region, transformedTris[i], aaSpans, opacity);
}
// Apply to surface (note: frees the AA spans)
if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) {
_rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
_apply(surface, aaSpans);
}
free(transformedTris);
return true;
}

View file

@ -641,8 +641,6 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
if (x + w > sw) w = (sw - x);
if (y + h > sh) h = (sh - y);
TVGLOG("SW_ENGINE", "Using intermediate composition [Region: %d %d %d %d]", x, y, w, h);
cmp->compositor->recoverSfc = surface;
cmp->compositor->recoverCmp = surface->compositor;
cmp->compositor->valid = false;

View file

@ -84,5 +84,6 @@ uint16_t THORVG_VERSION_NUMBER();
#define P(A) ((A)->pImpl) //Access to pimpl.
#define PP(A) (((Paint*)(A))->pImpl) //Access to pimpl.
#endif //_TVG_COMMON_H_

View file

@ -166,7 +166,7 @@ bool Paint::Impl::render(RenderMethod& renderer)
Create a composition image. */
if (compData && compData->method != CompositeMethod::ClipPath && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) {
auto region = smethod->bounds(renderer);
if (MASK_OPERATION(compData->method)) region.add(compData->target->pImpl->smethod->bounds(renderer));
if (MASK_REGION_MERGING(compData->method)) region.add(compData->target->pImpl->smethod->bounds(renderer));
if (region.w == 0 || region.h == 0) return true;
cmp = renderer.target(region, COMPOSITE_TO_COLORSPACE(renderer, compData->method));
if (renderer.beginComposite(cmp, CompositeMethod::None, 255)) {
@ -206,23 +206,20 @@ RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pT
auto method = compData->method;
target->pImpl->ctxFlag &= ~ContextFlag::FastTrack; //reset
/* If transform has no rotation factors && ClipPath / AlphaMasking is a simple rectangle,
we can avoid regular ClipPath / AlphaMasking sequence but use viewport for performance */
/* If the transformation has no rotational factors and the ClipPath/Alpha(InvAlpha)Masking involves a simple rectangle,
we can optimize by using the viewport instead of the regular ClipPath/AlphaMasking sequence for improved performance. */
auto tryFastTrack = false;
if (target->identifier() == TVG_CLASS_ID_SHAPE) {
if (method == CompositeMethod::ClipPath) tryFastTrack = true;
//OPTIMIZE HERE: Actually, this condition AlphaMask is useless. We can skip it?
else if (method == CompositeMethod::AlphaMask) {
else {
auto shape = static_cast<Shape*>(target);
uint8_t a;
shape->fillColor(nullptr, nullptr, nullptr, &a);
if (a == 255 && shape->opacity() == 255 && !shape->fill()) tryFastTrack = true;
//OPTIMIZE HERE: Actually, this condition InvAlphaMask is useless. We can skip it?
} else if (method == CompositeMethod::InvAlphaMask) {
auto shape = static_cast<Shape*>(target);
uint8_t a;
shape->fillColor(nullptr, nullptr, nullptr, &a);
if ((a == 0 || shape->opacity() == 0) && !shape->fill()) tryFastTrack = true;
//no gradient fill & no compositions of the composition target.
if (!shape->fill() && !(PP(shape)->compData)) {
if (method == CompositeMethod::AlphaMask && a == 255 && PP(shape)->opacity == 255) tryFastTrack = true;
else if (method == CompositeMethod::InvAlphaMask && (a == 0 || PP(shape)->opacity == 0)) tryFastTrack = true;
}
}
if (tryFastTrack) {
RenderRegion viewport2;

View file

@ -255,21 +255,22 @@ public:
virtual bool endComposite(Compositor* cmp) = 0;
};
static inline bool MASK_OPERATION(CompositeMethod method)
static inline bool MASK_REGION_MERGING(CompositeMethod method)
{
switch(method) {
case CompositeMethod::AlphaMask:
case CompositeMethod::InvAlphaMask:
case CompositeMethod::LumaMask:
case CompositeMethod::InvLumaMask:
return false;
case CompositeMethod::AddMask:
case CompositeMethod::SubtractMask:
case CompositeMethod::IntersectMask:
return false;
//these might expand the rendering region
case CompositeMethod::AddMask:
case CompositeMethod::DifferenceMask:
return true;
default:
TVGERR("RENDERER", "Unsupported Composite Size! = %d", (int)method);
TVGERR("RENDERER", "Unsupported Composite Method! = %d", (int)method);
return false;
}
}