evas: improve evas map anti-alising rendering quality.

Old version algorithm was imperfection a bit, quality was poor at some specific
degrees, specifically, when pixel increment pattern on the diagonal lines is
unstable.

This revised version was better than old one even source code is much cleaner
and simpler.

See belows.

*NonAA vs AA:
https://ibb.co/bCNfMc

*Compare the worst case aa in the old version:
https://ibb.co/bEJsZx

*Test video:
https://youtu.be/Wn20Tym5lfg
This commit is contained in:
Hermet Park 2018-04-10 11:09:35 +09:00
parent 0088189eee
commit d62ee3bea1
1 changed files with 177 additions and 233 deletions

View File

@ -1,113 +1,53 @@
#define READY_TX() \
#define PUSH_EDGE_POINT() \
do \
{ \
if (eidx == 0) \
{ \
tx[0] = edge2.x; \
tx[1] = spans[y].span[0].x[0]; \
} \
else \
{ \
tx[0] = spans[y].span[0].x[1]; \
tx[1] = edge2.x; \
} \
p_edge.x = spans[y].span[0].x[eidx]; \
p_edge.y = y; \
ptx[0] = tx[0]; \
ptx[1] = tx[1]; \
} while (0)
static void
calc_irregular_coverage(Line* spans, int eidx, int y, int diagonal,
int edge_dist, Eina_Bool reverse)
{
if (eidx == 1) reverse = !reverse;
int coverage = (256 / (diagonal + 2));
int tmp;
for (int ry = 0; ry < (diagonal + 2); ry++)
{
tmp = y - ry - edge_dist;
if (tmp < 0) return;
spans[tmp].aa_len[eidx] = 1;
if (reverse) spans[tmp].aa_cov[eidx] = 256 - (coverage * ry);
else spans[tmp].aa_cov[eidx] = (coverage * ry);
}
}
#define READY_TX2() \
{ \
if (eidx == 0) \
{ \
tx2[0] = edge2.x; \
tx2[1] = edge1.x; \
} \
else \
{ \
tx2[0] = edge1.x; \
tx2[1] = edge2.x; \
} \
static void
calc_vert_coverage(Line *spans, int eidx, int y, int rewind, Eina_Bool reverse)
{
if (eidx == 1) reverse = !reverse;
int coverage = (256 / (rewind + 1));
int tmp;
for (int ry = 1; ry < (rewind + 1); ry++)
{
tmp = y - ry;
if (tmp < 0 ) return;
spans[tmp].aa_len[eidx] = 1;
if (reverse) spans[tmp].aa_cov[eidx] = (256 - (coverage * ry));
else spans[tmp].aa_cov[eidx] = (coverage * ry);
}
}
#define PUSH_EDGES(xx) \
{ \
if (!leftover) \
{ \
edge1.x = edge2.x; \
edge1.y = edge2.y; \
edge2.x = (xx); \
edge2.y = y; \
} \
else \
{ \
edge1.y = edge2.y; \
edge2.y = y; \
} \
reset_tx2 = EINA_TRUE; \
}
//Vertical Inside Direction
#define VERT_INSIDE(rewind, y_advance) \
{ \
int cov_range = edge2.y - edge1.y; \
int coverage = (256 / (cov_range + 1)); \
int ry; \
int val; \
for (ry = 1; ry < ((rewind) + 1); ry++) \
{ \
int ridx = (y - ry) + (y_advance); \
if (spans[ridx].aa_len[eidx] > 1) continue; \
if (eidx == 1) \
val = (256 - (coverage * (ry + (cov_range - (rewind))))); \
else \
val = (coverage * (ry + (cov_range - (rewind)))); \
if ((spans[ridx].aa_len[eidx] == 0) || \
(val < spans[ridx].aa_cov[eidx])) \
spans[ridx].aa_cov[eidx] = val; \
spans[ridx].aa_len[eidx] = 1; \
} \
prev_aa = 4; \
}
//Vertical Outside Direction
#define VERT_OUTSIDE(rewind, y_advance, cov_range) \
{ \
int coverage = (256 / ((cov_range) + 1)); \
int ry = 1; \
for (; ry < ((rewind) + 1); ry++) \
{ \
int ridx = (y - ry) + (y_advance); \
if (spans[ridx].aa_len[(eidx)] > 1) continue; \
spans[ridx].aa_len[(eidx)] = 1; \
if (eidx == 1) \
{ \
spans[ridx].aa_cov[(eidx)] = \
(coverage * (ry + (cov_range - (rewind)))); \
} \
else \
{ \
spans[ridx].aa_cov[(eidx)] = \
(256 - (coverage * (ry + ((cov_range) - (rewind))))); \
} \
} \
prev_aa = 2; \
}
//Horizontal Inside Direction
#define HORIZ_INSIDE(yy, xx, xx2) \
{ \
if (((xx) - (xx2)) > spans[(yy)].aa_len[(eidx)]) \
{ \
spans[(yy)].aa_len[(eidx)] = ((xx) - (xx2)); \
spans[(yy)].aa_cov[(eidx)] = (256 / (spans[(yy)].aa_len[(eidx)] + 1)); \
} \
}
//Horizontal Outside Direction
#define HORIZ_OUTSIDE(yy, xx, xx2) \
{ \
if (((xx) - (xx2)) > spans[(yy)].aa_len[(eidx)]) \
{ \
spans[(yy)].aa_len[(eidx)] = ((xx) - (xx2)); \
spans[(yy)].aa_cov[(eidx)] = (256 / (spans[(yy)].aa_len[(eidx)] + 1)); \
} \
static void
calc_horiz_coverage(Line *spans, int eidx, int y, int x, int x2)
{
if (spans[y].aa_len[eidx] < abs(x - x2))
{
spans[y].aa_len[eidx] = abs(x - x2);
spans[y].aa_cov[eidx] = (256 / (spans[y].aa_len[eidx] + 1));
}
}
static inline DATA32
@ -130,29 +70,31 @@ _aa_coverage_apply(Line *line, int ww, int w, DATA32 val, Eina_Bool src_alpha)
if (((val & 0xff000000) >> 24) < 0xff)
return (val | 0xff000000);
}
return val;
}
void
static void
_calc_aa_edges_internal(Line *spans, int eidx, int ystart, int yend)
{
int y;
Evas_Coord_Point edge1 = { -1, -1 }; //prev-previous edge pixel
Evas_Coord_Point edge2 = { -1, -1 }; //previous edge pixel
Evas_Coord_Point p_edge = {-1, -1}; //previous edge point
Evas_Coord_Point edge_diff = {0, 0}; //temporary used for point distance
/* store larger to tx[0] between prev and current edge's x positions. */
/* store bigger to tx[0] between prev and current edge's x positions. */
int tx[2] = {0, 0};
/* back up prev tx values */
int ptx[2] = {0, 0};
int diagonal = 0; //straight diagonal pixels counti
/* store lager to tx2[0] between edge1 and edge2's x positions. */
int tx2[2] = {0, 0};
//Previous edge direction:
#define DirOutHor 0x0011
#define DirOutVer 0x0001
#define DirInHor 0x0010
#define DirInVer 0x0000
#define DirNone 0x1000
/* previous edge anti-aliased type.
2: vertical outside
4: vertical inside */
int prev_aa = 0;
Eina_Bool reset_tx2 = EINA_TRUE;
int prev_dir = DirNone;
int cur_dir = DirNone;
yend -= ystart;
@ -160,129 +102,131 @@ _calc_aa_edges_internal(Line *spans, int eidx, int ystart, int yend)
for (y = 0; y < yend; y++)
{
if (spans[y].span[0].x[0] == -1) continue;
edge1.x = edge2.x = spans[y].span[0].x[eidx];
edge1.y = edge2.y = y;
p_edge.x = spans[y].span[0].x[eidx];
p_edge.y = y;
break;
}
//Calculates AA Edges
for (y++; y <= yend; y++)
for (y++; y < yend; y++)
{
Eina_Bool leftover = EINA_FALSE;
if (spans[y].span[0].x[eidx] == -1) leftover = EINA_TRUE;
if (!leftover) READY_TX()
//Case1. Outside Incremental
if (tx[0] > tx[1])
{
//Horizontal Edge
if ((y - edge2.y) == 1)
{
HORIZ_OUTSIDE(y, tx[0], tx[1])
}
//Vertical Edge
else if (tx[0] > tx[1])
{
VERT_OUTSIDE((y - edge2.y), 0, (y - edge2.y))
//Just in case: 1 pixel alias next to vertical edge?
if (abs(spans[(y + 1)].span[0].x[eidx] -
spans[y].span[0].x[eidx]) >= 1)
{
HORIZ_OUTSIDE(y, tx[0], tx[1])
}
}
PUSH_EDGES(spans[y].span[0].x[eidx])
}
//Case2. Inside Incremental
else if (tx[1] > tx[0])
{
//Just in case: direction is reversed at the outside vertical edge?
if (prev_aa == 2)
{
VERT_OUTSIDE((y - edge2.y), 0, (y - edge2.y))
edge1.x = spans[y - 1].span[0].x[eidx];
edge1.y = y - 1;
edge2.x = spans[y].span[0].x[eidx];
edge2.y = y;
}
else
PUSH_EDGES(spans[y].span[0].x[eidx])
/* Find next edge. We go forward 2 more index since this logic
computes aa edges by looking back in advance 2 spans. */
for (y++; y <= (yend + 2); y++)
{
leftover = EINA_FALSE;
if ((spans[y].span[0].x[eidx] == -1) || (y > yend))
leftover = EINA_TRUE;
if (!leftover) READY_TX()
if (reset_tx2) READY_TX2()
//Case 1. Inside Direction
if (tx[1] > tx[0])
{
//Horizontal Edge
if ((edge2.y - edge1.y) == 1)
{
HORIZ_INSIDE(edge1.y, tx2[0], tx2[1]);
}
//Vertical Edge
else if ((tx2[0] - tx2[1]) == 1)
{
VERT_INSIDE((edge2.y - edge1.y), -(y - edge2.y))
}
//Just in case: Right Side Square Edge...?
else if (prev_aa == 4)
{
VERT_INSIDE((edge2.y - edge1.y), -(y - edge2.y))
if ((y - edge2.y) == 1)
{
HORIZ_INSIDE((edge2.y - 1), edge2.x,
spans[y].span[0].x[eidx]);
}
}
PUSH_EDGES(spans[y].span[0].x[eidx])
}
//Case 2. Reversed. Outside Direction
else if (tx[1] < tx[0])
{
//Horizontal Edge
if ((edge2.y - edge1.y) == 1)
HORIZ_INSIDE(edge1.y, tx2[0], tx2[1])
//Vertical Edge
else
VERT_INSIDE((edge2.y - edge1.y), -(y - edge2.y))
PUSH_EDGES(spans[y].span[0].x[eidx])
break;
}
}
}
}
y = yend;
//Leftovers for verticals.
if (prev_aa == 2)
{
if (((eidx == 0) && (edge1.x > edge2.x)) ||
((eidx == 1) && (edge1.x < edge2.x)))
VERT_OUTSIDE((y - edge2.y + 1), 1, (edge2.y - edge1.y));
}
else if (prev_aa == 4)
{
if (((eidx == 0) && (edge1.x < edge2.x)) ||
((eidx == 1) && (edge1.x > edge2.x)))
//Ready tx
if (eidx == 0)
{
VERT_INSIDE((edge2.y - edge1.y), -(y - edge2.y))
VERT_INSIDE((y - edge2.y) + 1, 1);
tx[0] = p_edge.x;
tx[1] = spans[y].span[0].x[0];
}
else
{
tx[0] = spans[y].span[0].x[1];
tx[1] = p_edge.x;
}
edge_diff.x = (tx[0] - tx[1]);
edge_diff.y = (y - p_edge.y);
//Confirm current edge direction
if (edge_diff.x > 0)
{
if (edge_diff.y == 1) cur_dir = DirOutHor;
else cur_dir = DirOutVer;
}
else if (edge_diff.x < 0)
{
if (edge_diff.y == 1) cur_dir = DirInHor;
else cur_dir = DirInVer;
}
else cur_dir = DirNone;
//straight diagonal increase
if ((cur_dir == prev_dir) && (y < yend))
{
if ((abs(edge_diff.x) == 1) && (edge_diff.y == 1))
{
++diagonal;
PUSH_EDGE_POINT();
continue;
}
}
switch (cur_dir)
{
case DirOutHor:
{
calc_horiz_coverage(spans, eidx, y, tx[0], tx[1]);
if (diagonal > 0)
{
calc_irregular_coverage(spans, eidx, y, diagonal, 0,
EINA_TRUE);
diagonal = 0;
}
/* Increment direction is changed:
Outside Vertical -> Outside Horizontal */
if (prev_dir == DirOutVer)
calc_horiz_coverage(spans, eidx, p_edge.y, ptx[0], ptx[1]);
PUSH_EDGE_POINT();
}
break;
case DirOutVer:
{
calc_vert_coverage(spans, eidx, y, edge_diff.y, EINA_TRUE);
if (diagonal > 0)
{
calc_irregular_coverage(spans, eidx, y, diagonal,
edge_diff.y, EINA_FALSE);
diagonal = 0;
}
/* Increment direction is changed:
Outside Horizontal -> Outside Vertical */
if (prev_dir == DirOutHor)
calc_horiz_coverage(spans, eidx, p_edge.y, ptx[0], ptx[1]);
PUSH_EDGE_POINT();
}
break;
case DirInHor:
{
calc_horiz_coverage(spans, eidx, (y - 1), tx[0], tx[1]);
if (diagonal > 0)
{
calc_irregular_coverage(spans, eidx, y, diagonal, 0,
EINA_FALSE);
diagonal = 0;
}
/* Increment direction is changed:
Outside Horizontal -> Inside Horizontal */
if (prev_dir == DirOutHor)
calc_horiz_coverage(spans, eidx, p_edge.y, ptx[0], ptx[1]);
PUSH_EDGE_POINT();
}
break;
case DirInVer:
{
calc_vert_coverage(spans, eidx, y, edge_diff.y, EINA_FALSE);
if (diagonal > 0)
{
calc_irregular_coverage(spans, eidx, y, diagonal,
edge_diff.y, EINA_TRUE);
diagonal = 0;
}
/* Increment direction is changed:
Outside Horizontal -> Inside Vertical */
if (prev_dir == DirOutHor)
calc_horiz_coverage(spans, eidx, p_edge.y, ptx[0], ptx[1]);
PUSH_EDGE_POINT();
}
break;
}
if (cur_dir != DirNone) prev_dir = cur_dir;
}
//leftovers...?
if ((edge_diff.y == 1) && (edge_diff.x != 0))
{
calc_horiz_coverage(spans, eidx, y - 1, ptx[0], ptx[1]);
calc_horiz_coverage(spans, eidx, y, tx[0], tx[1]);
}
else
calc_vert_coverage(spans, eidx, (y + 1), (edge_diff.y + 2),
(prev_dir & 0x00000001));
}
static void