diff --git a/src/examples/PictureJpg.cpp b/src/examples/PictureJpg.cpp index 0dc6d35c..853b61e3 100644 --- a/src/examples/PictureJpg.cpp +++ b/src/examples/PictureJpg.cpp @@ -31,10 +31,10 @@ void tvgDrawCmds(tvg::Canvas* canvas) { if (!canvas) return; - auto opacity = 51; + auto opacity = 36; //Load jpg file from path - for (int i = 0; i < 5; ++i) { + for (int i = 0; i < 7; ++i) { auto picture = tvg::Picture::gen(); if (picture->load(EXAMPLE_DIR"/test.jpg") != tvg::Result::Success) { cout << "JPG is not supported. Did you enable JPG Loader?" << endl; diff --git a/src/examples/PicturePng.cpp b/src/examples/PicturePng.cpp index 3798ab15..fae0d489 100644 --- a/src/examples/PicturePng.cpp +++ b/src/examples/PicturePng.cpp @@ -38,9 +38,9 @@ void tvgDrawCmds(tvg::Canvas* canvas) canvas->push(move(bg)); //Load png file from path - auto opacity = 51; + auto opacity = 31; - for (int i = 0; i < 5; ++i) { + for (int i = 0; i < 7; ++i) { auto picture = tvg::Picture::gen(); if (picture->load(EXAMPLE_DIR"/test.png") != tvg::Result::Success) { cout << "PNG is not supported. Did you enable PNG Loader?" << endl; diff --git a/src/lib/sw_engine/tvgSwRasterTexmap.h b/src/lib/sw_engine/tvgSwRasterTexmap.h index c7d225d3..ece14ebe 100644 --- a/src/lib/sw_engine/tvgSwRasterTexmap.h +++ b/src/lib/sw_engine/tvgSwRasterTexmap.h @@ -31,6 +31,19 @@ struct Polygon Vertex vertex[3]; }; +struct AALine +{ + int32_t x[2]; + int32_t coverage[2]; + int32_t length[2]; +}; + +struct AASpans +{ + AALine *lines; + int32_t yStart; + int32_t yEnd; +}; static inline void _swap(float& a, float& b, float& tmp) { @@ -45,7 +58,29 @@ static float dxdya, dxdyb, dudya, dvdya; static float xa, xb, ua, va; -static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t opacity, uint32_t (*blendMethod)(uint32_t)) +//Y Range exception handling +static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, int& yEnd) +{ + int32_t regionTop, regionBottom; + + if (region) { + regionTop = region->min.y; + regionBottom = region->max.y; + } else { + regionTop = image->rle->spans->y; + regionBottom = image->rle->spans[image->rle->size - 1].y; + } + + if (yStart >= regionBottom) return false; + + if (yStart < regionTop) yStart = regionTop; + if (yEnd > regionBottom) yEnd = regionBottom; + + return true; +} + + +static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t opacity, uint32_t (*blendMethod)(uint32_t), AASpans* aaSpans) { #define TEXMAP_TRANSLUCENT #define TEXMAP_MASKING @@ -55,7 +90,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, } -static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t (*blendMethod)(uint32_t)) +static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t (*blendMethod)(uint32_t), AASpans* aaSpans) { #define TEXMAP_MASKING #include "tvgSwRasterTexmapInternal.h" @@ -63,7 +98,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, } -static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t opacity) +static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t opacity, AASpans* aaSpans) { #define TEXMAP_TRANSLUCENT #include "tvgSwRasterTexmapInternal.h" @@ -71,15 +106,14 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, } -static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd) +static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans) { #include "tvgSwRasterTexmapInternal.h" } - /* This mapping algorithm is based on Mikael Kalms's. */ -static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const SwBBox* region, uint32_t opacity, Polygon& polygon, uint32_t (*blendMethod)(uint32_t)) +static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const SwBBox* region, uint32_t opacity, Polygon& polygon, uint32_t (*blendMethod)(uint32_t), AASpans* aaSpans) { 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}; @@ -168,11 +202,11 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const xb = x[0] + dy * dxdyb + (off_y * dxdyb); if (blendMethod) { - if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], blendMethod); - else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blendMethod); + if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], blendMethod, aaSpans); + else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blendMethod, aaSpans); } else { - if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1]); - else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity); + if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans); + else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, aaSpans); } upper = true; @@ -189,11 +223,11 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const dxdyb = dxdy[2]; xb = x[1] + (1 - (y[1] - yi[1])) * dxdyb + (off_y * dxdyb); if (blendMethod) { - if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], blendMethod); - else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blendMethod); + if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], blendMethod, aaSpans); + else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blendMethod, aaSpans); } else { - if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2]); - else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity); + if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans); + else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, aaSpans); } } //Longer edge is on the right side @@ -218,11 +252,11 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const va = v[0] + dy * dvdya + (off_y * dvdya); if (blendMethod) { - if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], blendMethod); - else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blendMethod); + if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], blendMethod, aaSpans); + else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blendMethod, aaSpans); } else { - if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1]); - else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity); + if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans); + else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, aaSpans); } upper = true; @@ -242,17 +276,284 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const va = v[1] + dy * dvdya + (off_y * dvdya); if (blendMethod) { - if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], blendMethod); - else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blendMethod); + if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], blendMethod, aaSpans); + else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blendMethod, aaSpans); } else { - if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2]); - else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity); + if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans); + else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, aaSpans); } } } } +static AASpans* _AASpans(const Vertex* vertices, const SwImage* image, const SwBBox* region) +{ + //Initialize Y range + float ys = FLT_MAX, ye = -1.0f; + + for (int i = 0; i < 4; i++) { + if (vertices[i].pt.y < ys) ys = vertices[i].pt.y; + if (vertices[i].pt.y > ye) ye = vertices[i].pt.y; + } + + auto yStart = static_cast(ys); + auto yEnd = static_cast(ye); + + if (!_arrange(image, region, yStart, yEnd)) return nullptr; + + auto aaSpans = static_cast(malloc(sizeof(AASpans))); + aaSpans->yStart = yStart; + aaSpans->yEnd = yEnd; + + //Initialize X range + auto height = yEnd - yStart; + + aaSpans->lines = static_cast(calloc(height, sizeof(AALine))); + + for (int32_t i = 0; i < height; i++) { + aaSpans->lines[i].x[0] = INT32_MAX; + aaSpans->lines[i].x[1] = INT32_MIN; + } + 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) +{ + if (lines[y].length[eidx] < abs(x - 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: + * www.hermet.pe.kr/122 (hermetpark@gmail.com) +*/ +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++) { + //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 bool _apply(SwSurface* surface, AASpans* aaSpans) +{ + auto y = aaSpans->yStart; + uint32_t pixel; + uint32_t* dst; + int32_t pos; + + //left side + _calcAAEdge(aaSpans, 0); + //right side + _calcAAEdge(aaSpans, 1); + + while (y < aaSpans->yEnd) { + auto line = &aaSpans->lines[y - aaSpans->yStart]; + auto width = line->x[1] - line->x[0]; + if (width > 0) { + auto offset = y * surface->stride; + + //Left edge + dst = surface->buffer + (offset + line->x[0]); + if (line->x[0] > 1) pixel = *(dst - 1); + else pixel = *dst; + + pos = 1; + while (pos <= line->length[0]) { + *dst = INTERPOLATE((line->coverage[0] * pos), *dst, pixel); + ++dst; + ++pos; + } + + //Right edge + dst = surface->buffer + (offset + line->x[1] - 1); + if (line->x[1] < (int32_t)(surface->w - 1)) pixel = *(dst + 1); + else pixel = *dst; + + pos = width; + while ((int32_t)(width - line->length[1]) < pos) { + *dst = INTERPOLATE(255 - (line->coverage[1] * (line->length[1] - (width - pos))), *dst, pixel); + --dst; + --pos; + } + } + y++; + } + + free(aaSpans->lines); + free(aaSpans); + + return true; +} + + /* 2 triangles constructs 1 mesh. below figure illustrates vert[4] index info. @@ -278,6 +579,9 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const for (int i = 0; i < 4; i++) mathMultiply(&vertices[i].pt, transform); + auto aaSpans = _AASpans(vertices, image, region); + if (!aaSpans) return true; + Polygon polygon; //Draw the first polygon @@ -285,14 +589,14 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const polygon.vertex[1] = vertices[1]; polygon.vertex[2] = vertices[3]; - _rasterPolygonImage(surface, image, region, opacity, polygon, blendMethod); + _rasterPolygonImage(surface, image, region, opacity, polygon, blendMethod, aaSpans); //Draw the second polygon polygon.vertex[0] = vertices[1]; polygon.vertex[1] = vertices[2]; polygon.vertex[2] = vertices[3]; - _rasterPolygonImage(surface, image, region, opacity, polygon, blendMethod); + _rasterPolygonImage(surface, image, region, opacity, polygon, blendMethod, aaSpans); - return true; + return _apply(surface, aaSpans); } \ No newline at end of file diff --git a/src/lib/sw_engine/tvgSwRasterTexmapInternal.h b/src/lib/sw_engine/tvgSwRasterTexmapInternal.h index 0a93530e..f7203e18 100644 --- a/src/lib/sw_engine/tvgSwRasterTexmapInternal.h +++ b/src/lib/sw_engine/tvgSwRasterTexmapInternal.h @@ -25,56 +25,51 @@ float _xa = xa, _xb = xb, _ua = ua, _va = va; auto sbuf = image->data; auto dbuf = surface->buffer; - int sw = static_cast(image->stride); - int sh = image->h; - int dw = surface->stride; - int x1, x2, x, y, ar, ab, iru, irv, px; - int vv = 0; - int uu = 0; + int32_t sw = static_cast(image->stride); + int32_t sh = image->h; + int32_t dw = surface->stride; + int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay; + int32_t vv = 0, uu = 0; + int32_t minx = 0, maxx = 0; float dx, u, v, iptr; uint32_t* buf; - int regionTop; - int regionBottom; SwSpan* span = nullptr; //used only when rle based. #ifdef TEXMAP_MASKING uint32_t* cmp; #endif - if (region) { - regionTop = region->min.y; - regionBottom = region->max.y; - } else { - regionTop = image->rle->spans->y; - regionBottom = image->rle->spans[image->rle->size - 1].y; - } - - //Range exception handling - if (yStart >= regionBottom) return; - if (yStart < regionTop) yStart = regionTop; - if (yEnd > regionBottom) yEnd = regionBottom; + if (!_arrange(image, region, yStart, yEnd)) return; //Loop through all lines in the segment y = yStart; - if (!region) span = image->rle->spans + (yStart - regionTop); + if (region) { + minx = region->min.x; + maxx = region->max.x; + } else { + span = image->rle->spans + (yStart - image->rle->spans->y); + } while (y < yEnd) { x1 = _xa; x2 = _xb; - //Range exception handling - if (region) { - if (x1 < region->min.x) x1 = region->min.x; - if (x2 > region->max.x) x2 = region->max.x; - if ((x2 - x1) < 1) goto next; - if ((x1 >= region->max.x) || (x2 <= region->min.x)) goto next; - } else { - if (x1 < span->x) x1 = span->x; - if (x2 > span->x + span->len) x2 = (span->x + span->len); - if ((x2 - x1) < 1) goto next; - if ((x1 >= (span->x + span->len)) || (x2 <= span->x)) goto next; + if (!region) { + minx = span->x; + maxx = span->x + span->len; } + + if (x1 < minx) x1 = minx; + if (x2 > maxx) x2 = maxx; + + //Anti-Aliasing frames + 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 exception + if ((x2 - x1) < 1 || (x1 >= maxx) || (x2 <= minx)) goto next; //Perform subtexel pre-stepping on UV dx = 1 - (_xa - x1); diff --git a/test/images/test.tvg b/test/images/test.tvg index 9a989b79..68a79851 100644 Binary files a/test/images/test.tvg and b/test/images/test.tvg differ