mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-14 12:04:29 +00:00
sw_engine: replaced texture mapping AA with 4x sampling interp
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
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
The old approach often produced incorrect results, especially when the fixed pixel had a noticeably different color from the texture due to the AA target blending position was fixed. Although the previous method worked well as an analytical AA solution with good speed and fair quality, it couldn't overcome the above limitation. The new approach still applies AA only to polygon edges for efficiency. While the quality may be slightly reduced, it offers greater stability. - binary size: -1.1kb - performance diff: ignoreable issue: https://github.com/thorvg/thorvg/issues/1729
This commit is contained in:
parent
596f7f767f
commit
174fae9089
1 changed files with 26 additions and 231 deletions
|
@ -33,9 +33,7 @@ struct Polygon
|
||||||
|
|
||||||
struct AALine
|
struct AALine
|
||||||
{
|
{
|
||||||
int32_t x[2];
|
uint16_t x[2];
|
||||||
int32_t coverage[2];
|
|
||||||
int32_t length[2];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AASpans
|
struct AASpans
|
||||||
|
@ -138,8 +136,8 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
|
||||||
//Anti-Aliasing frames
|
//Anti-Aliasing frames
|
||||||
if (aaSpans) {
|
if (aaSpans) {
|
||||||
ay = y - aaSpans->yStart;
|
ay = y - aaSpans->yStart;
|
||||||
if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
|
if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = static_cast<uint16_t>(x1);
|
||||||
if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
|
if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = static_cast<uint16_t>(x2);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Range allowed
|
//Range allowed
|
||||||
|
@ -149,9 +147,7 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
|
||||||
dx = 1 - (_xa - x1);
|
dx = 1 - (_xa - x1);
|
||||||
u = _ua + dx * _dudx;
|
u = _ua + dx * _dudx;
|
||||||
v = _va + dx * _dvdx;
|
v = _va + dx * _dvdx;
|
||||||
|
|
||||||
buf = dbuf + ((y * surface->stride) + x1);
|
buf = dbuf + ((y * surface->stride) + x1);
|
||||||
|
|
||||||
x = x1;
|
x = x1;
|
||||||
|
|
||||||
//Draw horizontal line
|
//Draw horizontal line
|
||||||
|
@ -273,12 +269,13 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
|
||||||
//Anti-Aliasing frames
|
//Anti-Aliasing frames
|
||||||
if (aaSpans) {
|
if (aaSpans) {
|
||||||
ay = y - aaSpans->yStart;
|
ay = y - aaSpans->yStart;
|
||||||
if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
|
if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = static_cast<uint16_t>(x1);
|
||||||
if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
|
if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = static_cast<uint16_t>(x2);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Range allowed
|
//Range allowed
|
||||||
if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) {
|
if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) {
|
||||||
|
|
||||||
//Perform subtexel pre-stepping on UV
|
//Perform subtexel pre-stepping on UV
|
||||||
dx = 1 - (_xa - x1);
|
dx = 1 - (_xa - x1);
|
||||||
u = _ua + dx * _dudx;
|
u = _ua + dx * _dudx;
|
||||||
|
@ -299,6 +296,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
|
||||||
|
|
||||||
ar = _modf(u);
|
ar = _modf(u);
|
||||||
ab = _modf(v);
|
ab = _modf(v);
|
||||||
|
|
||||||
iru = uu + 1;
|
iru = uu + 1;
|
||||||
irv = vv + 1;
|
irv = vv + 1;
|
||||||
|
|
||||||
|
@ -547,243 +545,40 @@ static AASpans* _AASpans(int yStart, int yEnd)
|
||||||
aaSpans->lines = tvg::malloc<AALine*>(height * sizeof(AALine));
|
aaSpans->lines = tvg::malloc<AALine*>(height * sizeof(AALine));
|
||||||
|
|
||||||
for (int32_t i = 0; i < height; i++) {
|
for (int32_t i = 0; i < height; i++) {
|
||||||
aaSpans->lines[i].x[0] = INT32_MAX;
|
aaSpans->lines[i].x[0] = UINT16_MAX;
|
||||||
aaSpans->lines[i].x[1] = 0;
|
aaSpans->lines[i].x[1] = 0;
|
||||||
aaSpans->lines[i].length[0] = 0;
|
|
||||||
aaSpans->lines[i].length[1] = 0;
|
|
||||||
}
|
}
|
||||||
return aaSpans;
|
return aaSpans;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void _calcIrregularCoverage(AALine* lines, int32_t eidx, int32_t y, int32_t diagonal, int32_t edgeDist, bool reverse)
|
|
||||||
{
|
|
||||||
if (eidx == 1) reverse = !reverse;
|
|
||||||
int32_t coverage = (255 / (diagonal + 2));
|
|
||||||
int32_t tmp;
|
|
||||||
for (int32_t ry = 0; ry < (diagonal + 2); ry++) {
|
|
||||||
tmp = y - ry - edgeDist;
|
|
||||||
if (tmp < 0) return;
|
|
||||||
lines[tmp].length[eidx] = 1;
|
|
||||||
if (reverse) lines[tmp].coverage[eidx] = 255 - (coverage * ry);
|
|
||||||
else lines[tmp].coverage[eidx] = (coverage * ry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void _calcVertCoverage(AALine *lines, int32_t eidx, int32_t y, int32_t rewind, bool reverse)
|
|
||||||
{
|
|
||||||
if (eidx == 1) reverse = !reverse;
|
|
||||||
int32_t coverage = (255 / (rewind + 1));
|
|
||||||
int32_t tmp;
|
|
||||||
for (int ry = 1; ry < (rewind + 1); ry++) {
|
|
||||||
tmp = y - ry;
|
|
||||||
if (tmp < 0) return;
|
|
||||||
lines[tmp].length[eidx] = 1;
|
|
||||||
if (reverse) lines[tmp].coverage[eidx] = (255 - (coverage * ry));
|
|
||||||
else lines[tmp].coverage[eidx] = (coverage * ry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void _calcHorizCoverage(AALine *lines, int32_t eidx, int32_t y, int32_t x, int32_t x2)
|
|
||||||
{
|
|
||||||
lines[y].length[eidx] = abs(x - x2);
|
|
||||||
lines[y].coverage[eidx] = (255 / (lines[y].length[eidx] + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This Anti-Aliasing mechanism is originated from Hermet Park's idea.
|
|
||||||
* To understand this AA logic, you can refer this page:
|
|
||||||
* https://uigraphics.tistory.com/1
|
|
||||||
*/
|
|
||||||
static void _calcAAEdge(AASpans *aaSpans, int32_t eidx)
|
|
||||||
{
|
|
||||||
//Previous edge direction:
|
|
||||||
#define DirOutHor 0x0011
|
|
||||||
#define DirOutVer 0x0001
|
|
||||||
#define DirInHor 0x0010
|
|
||||||
#define DirInVer 0x0000
|
|
||||||
#define DirNone 0x1000
|
|
||||||
|
|
||||||
#define PUSH_VERTEX() \
|
|
||||||
do { \
|
|
||||||
pEdge.x = lines[y].x[eidx]; \
|
|
||||||
pEdge.y = y; \
|
|
||||||
ptx[0] = tx[0]; \
|
|
||||||
ptx[1] = tx[1]; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
int32_t y = 0;
|
|
||||||
SwPoint pEdge = {-1, -1}; //previous edge point
|
|
||||||
SwPoint edgeDiff = {0, 0}; //temporary used for point distance
|
|
||||||
|
|
||||||
/* store bigger to tx[0] between prev and current edge's x positions. */
|
|
||||||
int32_t tx[2] = {0, 0};
|
|
||||||
/* back up prev tx values */
|
|
||||||
int32_t ptx[2] = {0, 0};
|
|
||||||
int32_t diagonal = 0; //straight diagonal pixels count
|
|
||||||
|
|
||||||
auto yStart = aaSpans->yStart;
|
|
||||||
auto yEnd = aaSpans->yEnd;
|
|
||||||
auto lines = aaSpans->lines;
|
|
||||||
|
|
||||||
int32_t prevDir = DirNone;
|
|
||||||
int32_t curDir = DirNone;
|
|
||||||
|
|
||||||
yEnd -= yStart;
|
|
||||||
|
|
||||||
//Start Edge
|
|
||||||
if (y < yEnd) {
|
|
||||||
pEdge.x = lines[y].x[eidx];
|
|
||||||
pEdge.y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Calculates AA Edges
|
|
||||||
for (y++; y < yEnd; y++) {
|
|
||||||
|
|
||||||
if (lines[y].x[0] == INT32_MAX) continue;
|
|
||||||
|
|
||||||
//Ready tx
|
|
||||||
if (eidx == 0) {
|
|
||||||
tx[0] = pEdge.x;
|
|
||||||
tx[1] = lines[y].x[0];
|
|
||||||
} else {
|
|
||||||
tx[0] = lines[y].x[1];
|
|
||||||
tx[1] = pEdge.x;
|
|
||||||
}
|
|
||||||
edgeDiff.x = (tx[0] - tx[1]);
|
|
||||||
edgeDiff.y = (y - pEdge.y);
|
|
||||||
|
|
||||||
//Confirm current edge direction
|
|
||||||
if (edgeDiff.x > 0) {
|
|
||||||
if (edgeDiff.y == 1) curDir = DirOutHor;
|
|
||||||
else curDir = DirOutVer;
|
|
||||||
} else if (edgeDiff.x < 0) {
|
|
||||||
if (edgeDiff.y == 1) curDir = DirInHor;
|
|
||||||
else curDir = DirInVer;
|
|
||||||
} else curDir = DirNone;
|
|
||||||
|
|
||||||
//straight diagonal increase
|
|
||||||
if ((curDir == prevDir) && (y < yEnd)) {
|
|
||||||
if ((abs(edgeDiff.x) == 1) && (edgeDiff.y == 1)) {
|
|
||||||
++diagonal;
|
|
||||||
PUSH_VERTEX();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (curDir) {
|
|
||||||
case DirOutHor: {
|
|
||||||
_calcHorizCoverage(lines, eidx, y, tx[0], tx[1]);
|
|
||||||
if (diagonal > 0) {
|
|
||||||
_calcIrregularCoverage(lines, eidx, y, diagonal, 0, true);
|
|
||||||
diagonal = 0;
|
|
||||||
}
|
|
||||||
/* Increment direction is changed: Outside Vertical -> Outside Horizontal */
|
|
||||||
if (prevDir == DirOutVer) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
|
|
||||||
|
|
||||||
//Trick, but fine-tunning!
|
|
||||||
if (y == 1) _calcHorizCoverage(lines, eidx, pEdge.y, tx[0], tx[1]);
|
|
||||||
PUSH_VERTEX();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DirOutVer: {
|
|
||||||
_calcVertCoverage(lines, eidx, y, edgeDiff.y, true);
|
|
||||||
if (diagonal > 0) {
|
|
||||||
_calcIrregularCoverage(lines, eidx, y, diagonal, edgeDiff.y, false);
|
|
||||||
diagonal = 0;
|
|
||||||
}
|
|
||||||
/* Increment direction is changed: Outside Horizontal -> Outside Vertical */
|
|
||||||
if (prevDir == DirOutHor) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
|
|
||||||
PUSH_VERTEX();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DirInHor: {
|
|
||||||
_calcHorizCoverage(lines, eidx, (y - 1), tx[0], tx[1]);
|
|
||||||
if (diagonal > 0) {
|
|
||||||
_calcIrregularCoverage(lines, eidx, y, diagonal, 0, false);
|
|
||||||
diagonal = 0;
|
|
||||||
}
|
|
||||||
/* Increment direction is changed: Outside Horizontal -> Inside Horizontal */
|
|
||||||
if (prevDir == DirOutHor) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
|
|
||||||
PUSH_VERTEX();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DirInVer: {
|
|
||||||
_calcVertCoverage(lines, eidx, y, edgeDiff.y, false);
|
|
||||||
if (prevDir == DirOutHor) edgeDiff.y -= 1; //Weird, fine tuning?????????????????????
|
|
||||||
if (diagonal > 0) {
|
|
||||||
_calcIrregularCoverage(lines, eidx, y, diagonal, edgeDiff.y, true);
|
|
||||||
diagonal = 0;
|
|
||||||
}
|
|
||||||
/* Increment direction is changed: Outside Horizontal -> Inside Vertical */
|
|
||||||
if (prevDir == DirOutHor) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
|
|
||||||
PUSH_VERTEX();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (curDir != DirNone) prevDir = curDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
//leftovers...?
|
|
||||||
if ((edgeDiff.y == 1) && (edgeDiff.x != 0)) {
|
|
||||||
if (y >= yEnd) y = (yEnd - 1);
|
|
||||||
_calcHorizCoverage(lines, eidx, y - 1, ptx[0], ptx[1]);
|
|
||||||
_calcHorizCoverage(lines, eidx, y, tx[0], tx[1]);
|
|
||||||
} else {
|
|
||||||
++y;
|
|
||||||
if (y > yEnd) y = yEnd;
|
|
||||||
_calcVertCoverage(lines, eidx, y, (edgeDiff.y + 1), (prevDir & 0x00000001));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void _apply(SwSurface* surface, AASpans* aaSpans)
|
static void _apply(SwSurface* surface, AASpans* aaSpans)
|
||||||
{
|
{
|
||||||
auto end = surface->buf32 + surface->h * surface->stride;
|
|
||||||
auto buf = surface->buf32 + surface->stride * aaSpans->yStart;
|
auto buf = surface->buf32 + surface->stride * aaSpans->yStart;
|
||||||
|
auto w = int32_t(surface->w - 1);
|
||||||
|
auto h = int32_t(surface->h - 1);
|
||||||
auto y = aaSpans->yStart;
|
auto y = aaSpans->yStart;
|
||||||
auto line = aaSpans->lines;
|
auto line = aaSpans->lines;
|
||||||
uint32_t pix;
|
|
||||||
uint32_t* dst;
|
|
||||||
int32_t pos;
|
|
||||||
|
|
||||||
_calcAAEdge(aaSpans, 0); //left side
|
constexpr int WEIGHT = 190;
|
||||||
_calcAAEdge(aaSpans, 1); //right side
|
|
||||||
|
auto feathering = [&](pixel_t* dst, int32_t x) {
|
||||||
|
if (y > 0) {
|
||||||
|
auto top = dst - surface->stride;
|
||||||
|
if (x < w) *dst = INTERPOLATE(*dst, *(top + 1), WEIGHT);
|
||||||
|
if (x > 0) *dst = INTERPOLATE(*dst, *(top - 1), WEIGHT);
|
||||||
|
}
|
||||||
|
if (y < h) {
|
||||||
|
auto bottom = dst + surface->stride;
|
||||||
|
if (x < w) *dst = INTERPOLATE(*dst, *(bottom + 1), WEIGHT);
|
||||||
|
if (x > 0) *dst = INTERPOLATE(*dst, *(bottom - 1), WEIGHT);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
while (y < aaSpans->yEnd) {
|
while (y < aaSpans->yEnd) {
|
||||||
if (line->x[1] - line->x[0] > 0) {
|
if (line->x[1] - line->x[0] > 0) {
|
||||||
//Left edge
|
feathering(buf + line->x[0], line->x[0]); //left
|
||||||
dst = buf + line->x[0];
|
feathering(buf + line->x[1] - 1, line->x[1] - 1); //right
|
||||||
pix = *(dst - ((line->x[0] > 1) ? 1 : 0));
|
|
||||||
pos = 1;
|
|
||||||
|
|
||||||
//exceptional handling. out of memory bound.
|
|
||||||
if (dst + line->length[0] >= end) {
|
|
||||||
pos += (dst + line->length[0] - end);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (pos <= line->length[0]) {
|
|
||||||
*dst = INTERPOLATE(*dst, pix, line->coverage[0] * pos);
|
|
||||||
++dst;
|
|
||||||
++pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Right edge
|
|
||||||
dst = buf + line->x[1] - 1;
|
|
||||||
pix = *(dst + (line->x[1] < (int32_t)(surface->w - 1) ? 1 : 0));
|
|
||||||
pos = line->length[1];
|
|
||||||
|
|
||||||
//exceptional handling. out of memory bound.
|
|
||||||
if (dst - pos < surface->buf32) --pos;
|
|
||||||
|
|
||||||
while (pos > 0) {
|
|
||||||
*dst = INTERPOLATE(*dst, pix, 255 - (line->coverage[1] * pos));
|
|
||||||
--dst;
|
|
||||||
--pos;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
buf += surface->stride;
|
buf += surface->stride;
|
||||||
++line;
|
++line;
|
||||||
|
|
Loading…
Add table
Reference in a new issue