summaryrefslogtreecommitdiffstats
path: root/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h')
-rw-r--r--thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h1207
1 files changed, 1207 insertions, 0 deletions
diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h
new file mode 100644
index 0000000000..698ab37da2
--- /dev/null
+++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h
@@ -0,0 +1,1207 @@
+/*
+ * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+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)
+{
+ tmp = a;
+ a = b;
+ b = tmp;
+}
+
+
+//Careful! Shared resource, No support threading
+static float dudx, dvdx;
+static float dxdya, dxdyb, dudya, dvdya;
+static float xa, xb, ua, va;
+
+
+//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 bool _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0)
+{
+ return false;
+
+#if 0 //Enable it when GRAYSCALE image is supported
+ auto maskOp = _getMaskOp(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;
+ auto sbuf = image->buf8;
+ int32_t sw = static_cast<int32_t>(image->stride);
+ int32_t sh = image->h;
+ int32_t x1, x2, x, y, 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;
+ SwSpan* span = nullptr; //used only when rle based.
+
+ if (!_arrange(image, region, yStart, yEnd)) return false;
+
+ //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;
+ }
+ }
+
+ y = yStart;
+
+ while (y < yEnd) {
+ 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
+ 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)) {
+
+ //Perform subtexel pre-stepping on UV
+ dx = 1 - (_xa - x1);
+ u = _ua + dx * _dudx;
+ v = _va + dx * _dvdx;
+
+ x = x1;
+
+ auto cmp = &surface->compositor->image.buf8[y * surface->compositor->image.stride + x1];
+ auto dst = &surface->buf8[y * surface->stride + x1];
+
+ if (opacity == 255) {
+ //Draw horizontal line
+ while (x++ < x2) {
+ 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);
+ }
+ if (direct) {
+ auto tmp = maskOp(px, *cmp, 0); //not use alpha
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
+ ++dst;
+ } else {
+ *cmp = maskOp(px, *cmp, ~px);
+ }
+ ++cmp;
+
+ //Step UV horizontally
+ u += _dudx;
+ v += _dvdx;
+ //range over?
+ if ((uint32_t)v >= image->h) break;
+ }
+ } else {
+ //Draw horizontal line
+ while (x++ < x2) {
+ 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);
+ }
+
+ if (direct) {
+ auto tmp = maskOp(MULTIPLY(px, opacity), *cmp, 0);
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
+ ++dst;
+ } else {
+ auto tmp = MULTIPLY(px, opacity);
+ *cmp = maskOp(tmp, *cmp, ~px);
+ }
+ ++cmp;
+
+ //Step UV horizontally
+ u += _dudx;
+ v += _dvdx;
+ //range over?
+ if ((uint32_t)v >= image->h) break;
+ }
+ }
+ }
+
+ //Step along both edges
+ _xa += _dxdya;
+ _xb += _dxdyb;
+ _ua += _dudya;
+ _va += _dvdya;
+
+ if (!region && spanIdx >= image->rle->size) break;
+
+ ++y;
+ }
+ xa = _xa;
+ xb = _xb;
+ ua = _ua;
+ va = _va;
+
+ return true;
+#endif
+}
+
+
+static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity)
+{
+ 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;
+ auto dbuf = surface->buf32;
+ int32_t sw = static_cast<int32_t>(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 = INT32_MAX, maxx = INT32_MIN;
+ float dx, u, v, iptr;
+ uint32_t* buf;
+ SwSpan* span = nullptr; //used only when rle based.
+
+ if (!_arrange(image, region, yStart, yEnd)) return;
+
+ //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;
+ }
+ }
+
+ y = yStart;
+
+ while (y < yEnd) {
+ 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
+ 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)) {
+
+ //Perform subtexel pre-stepping on UV
+ dx = 1 - (_xa - x1);
+ u = _ua + dx * _dudx;
+ v = _va + dx * _dvdx;
+
+ buf = dbuf + ((y * dw) + x1);
+
+ x = x1;
+
+ if (opacity == 255) {
+ //Draw horizontal line
+ while (x++ < x2) {
+ 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);
+ }
+ *buf = surface->blender(px, *buf, IA(px));
+ ++buf;
+
+ //Step UV horizontally
+ u += _dudx;
+ v += _dvdx;
+ //range over?
+ if ((uint32_t)v >= image->h) break;
+ }
+ } else {
+ //Draw horizontal line
+ while (x++ < x2) {
+ 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);
+ }
+ auto src = ALPHA_BLEND(px, opacity);
+ *buf = surface->blender(src, *buf, IA(src));
+ ++buf;
+
+ //Step UV horizontally
+ u += _dudx;
+ v += _dvdx;
+ //range over?
+ if ((uint32_t)v >= image->h) break;
+ }
+ }
+ }
+
+ //Step along both edges
+ _xa += _dxdya;
+ _xb += _dxdyb;
+ _ua += _dudya;
+ _va += _dvdya;
+
+ if (!region && spanIdx >= image->rle->size) break;
+
+ ++y;
+ }
+ xa = _xa;
+ xb = _xb;
+ ua = _ua;
+ va = _va;
+}
+
+
+static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, bool matting)
+{
+ 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;
+ auto dbuf = surface->buf32;
+ int32_t sw = static_cast<int32_t>(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 = INT32_MAX, maxx = INT32_MIN;
+ float dx, u, v, iptr;
+ uint32_t* buf;
+ SwSpan* span = nullptr; //used only when rle based.
+
+ //for matting(composition)
+ auto csize = matting ? surface->compositor->image.channelSize: 0;
+ auto alpha = matting ? surface->alpha(surface->compositor->method) : nullptr;
+ uint8_t* cmp = nullptr;
+
+ if (!_arrange(image, region, yStart, yEnd)) return;
+
+ //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;
+ }
+ }
+
+ y = yStart;
+
+ while (y < yEnd) {
+ 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
+ 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)) {
+
+ //Perform subtexel pre-stepping on UV
+ dx = 1 - (_xa - x1);
+ u = _ua + dx * _dudx;
+ v = _va + dx * _dvdx;
+
+ buf = dbuf + ((y * dw) + x1);
+
+ x = x1;
+
+ if (matting) cmp = &surface->compositor->image.buf8[(y * surface->compositor->image.stride + x1) * csize];
+
+ if (opacity == 255) {
+ //Draw horizontal line
+ while (x++ < x2) {
+ 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);
+ }
+ uint32_t src;
+ if (matting) {
+ src = ALPHA_BLEND(px, alpha(cmp));
+ cmp += csize;
+ } else {
+ src = px;
+ }
+ *buf = src + ALPHA_BLEND(*buf, IA(src));
+ ++buf;
+
+ //Step UV horizontally
+ u += _dudx;
+ v += _dvdx;
+ //range over?
+ if ((uint32_t)v >= image->h) break;
+ }
+ } else {
+ //Draw horizontal line
+ while (x++ < x2) {
+ uu = (int) u;
+ vv = (int) v;
+
+ ar = (int)(255 * (1 - modff(u, &iptr)));
+ ab = (int)(255 * (1 - modff(v, &iptr)));
+ iru = uu + 1;
+ irv = vv + 1;
+
+ if (vv >= sh) continue;
+
+ 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);
+ }
+ uint32_t src;
+ if (matting) {
+ src = ALPHA_BLEND(px, MULTIPLY(opacity, alpha(cmp)));
+ cmp += csize;
+ } else {
+ src = ALPHA_BLEND(px, opacity);
+ }
+ *buf = src + ALPHA_BLEND(*buf, IA(src));
+ ++buf;
+
+ //Step UV horizontally
+ u += _dudx;
+ v += _dvdx;
+ //range over?
+ if ((uint32_t)v >= image->h) break;
+ }
+ }
+ }
+
+ //Step along both edges
+ _xa += _dxdya;
+ _xb += _dxdyb;
+ _ua += _dudya;
+ _va += _dvdya;
+
+ if (!region && spanIdx >= image->rle->size) break;
+
+ ++y;
+ }
+ xa = _xa;
+ xb = _xb;
+ ua = _ua;
+ va = _va;
+}
+
+
+/* This mapping algorithm is based on Mikael Kalms's. */
+static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const SwBBox* region, Polygon& polygon, AASpans* aaSpans, uint8_t opacity)
+{
+ float x[3] = {polygon.vertex[0].pt.x, polygon.vertex[1].pt.x, polygon.vertex[2].pt.x};
+ float y[3] = {polygon.vertex[0].pt.y, polygon.vertex[1].pt.y, polygon.vertex[2].pt.y};
+ float u[3] = {polygon.vertex[0].uv.x, polygon.vertex[1].uv.x, polygon.vertex[2].uv.x};
+ float v[3] = {polygon.vertex[0].uv.y, polygon.vertex[1].uv.y, polygon.vertex[2].uv.y};
+
+ float off_y;
+ float dxdy[3] = {0.0f, 0.0f, 0.0f};
+ float tmp;
+
+ auto upper = false;
+
+ //Sort the vertices in ascending Y order
+ if (y[0] > y[1]) {
+ _swap(x[0], x[1], tmp);
+ _swap(y[0], y[1], tmp);
+ _swap(u[0], u[1], tmp);
+ _swap(v[0], v[1], tmp);
+ }
+ if (y[0] > y[2]) {
+ _swap(x[0], x[2], tmp);
+ _swap(y[0], y[2], tmp);
+ _swap(u[0], u[2], tmp);
+ _swap(v[0], v[2], tmp);
+ }
+ if (y[1] > y[2]) {
+ _swap(x[1], x[2], tmp);
+ _swap(y[1], y[2], tmp);
+ _swap(u[1], u[2], tmp);
+ _swap(v[1], v[2], tmp);
+ }
+
+ //Y indexes
+ int yi[3] = {(int)y[0], (int)y[1], (int)y[2]};
+
+ //Skip drawing if it's too thin to cover any pixels at all.
+ if ((yi[0] == yi[1] && yi[0] == yi[2]) || ((int) x[0] == (int) x[1] && (int) x[0] == (int) x[2])) return;
+
+ //Calculate horizontal and vertical increments for UV axes (these calcs are certainly not optimal, although they're stable (handles any dy being 0)
+ auto denom = ((x[2] - x[0]) * (y[1] - y[0]) - (x[1] - x[0]) * (y[2] - y[0]));
+
+ //Skip poly if it's an infinitely thin line
+ if (mathZero(denom)) return;
+
+ denom = 1 / denom; //Reciprocal for speeding up
+ dudx = ((u[2] - u[0]) * (y[1] - y[0]) - (u[1] - u[0]) * (y[2] - y[0])) * denom;
+ dvdx = ((v[2] - v[0]) * (y[1] - y[0]) - (v[1] - v[0]) * (y[2] - y[0])) * denom;
+ auto dudy = ((u[1] - u[0]) * (x[2] - x[0]) - (u[2] - u[0]) * (x[1] - x[0])) * denom;
+ auto dvdy = ((v[1] - v[0]) * (x[2] - x[0]) - (v[2] - v[0]) * (x[1] - x[0])) * denom;
+
+ //Calculate X-slopes along the edges
+ if (y[1] > y[0]) dxdy[0] = (x[1] - x[0]) / (y[1] - y[0]);
+ if (y[2] > y[0]) dxdy[1] = (x[2] - x[0]) / (y[2] - y[0]);
+ if (y[2] > y[1]) dxdy[2] = (x[2] - x[1]) / (y[2] - y[1]);
+
+ //Determine which side of the polygon the longer edge is on
+ auto side = (dxdy[1] > dxdy[0]) ? true : false;
+
+ if (mathEqual(y[0], y[1])) side = x[0] > x[1];
+ if (mathEqual(y[1], y[2])) side = x[2] > x[1];
+
+ auto regionTop = region ? region->min.y : image->rle->spans->y; //Normal Image or Rle Image?
+ auto compositing = _compositing(surface); //Composition required
+ auto blending = _blending(surface); //Blending required
+
+ //Longer edge is on the left side
+ if (!side) {
+ //Calculate slopes along left edge
+ dxdya = dxdy[1];
+ dudya = dxdya * dudx + dudy;
+ dvdya = dxdya * dvdx + dvdy;
+
+ //Perform subpixel pre-stepping along left edge
+ auto dy = 1.0f - (y[0] - yi[0]);
+ xa = x[0] + dy * dxdya;
+ ua = u[0] + dy * dudya;
+ va = v[0] + dy * dvdya;
+
+ //Draw upper segment if possibly visible
+ if (yi[0] < yi[1]) {
+ off_y = y[0] < regionTop ? (regionTop - y[0]) : 0;
+ xa += (off_y * dxdya);
+ ua += (off_y * dudya);
+ va += (off_y * dvdya);
+
+ // Set right edge X-slope and perform subpixel pre-stepping
+ dxdyb = dxdy[0];
+ xb = x[0] + dy * dxdyb + (off_y * dxdyb);
+
+ if (compositing) {
+ if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true);
+ else _rasterMaskedPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, 1);
+ } else if (blending) {
+ _rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity);
+ } else {
+ _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, false);
+ }
+ upper = true;
+ }
+ //Draw lower segment if possibly visible
+ if (yi[1] < yi[2]) {
+ off_y = y[1] < regionTop ? (regionTop - y[1]) : 0;
+ if (!upper) {
+ xa += (off_y * dxdya);
+ ua += (off_y * dudya);
+ va += (off_y * dvdya);
+ }
+ // Set right edge X-slope and perform subpixel pre-stepping
+ dxdyb = dxdy[2];
+ xb = x[1] + (1 - (y[1] - yi[1])) * dxdyb + (off_y * dxdyb);
+ if (compositing) {
+ if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true);
+ else _rasterMaskedPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, 2);
+ } else if (blending) {
+ _rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity);
+ } else {
+ _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, false);
+ }
+ }
+ //Longer edge is on the right side
+ } else {
+ //Set right edge X-slope and perform subpixel pre-stepping
+ dxdyb = dxdy[1];
+ auto dy = 1.0f - (y[0] - yi[0]);
+ xb = x[0] + dy * dxdyb;
+
+ //Draw upper segment if possibly visible
+ if (yi[0] < yi[1]) {
+ off_y = y[0] < regionTop ? (regionTop - y[0]) : 0;
+ xb += (off_y *dxdyb);
+
+ // Set slopes along left edge and perform subpixel pre-stepping
+ dxdya = dxdy[0];
+ dudya = dxdya * dudx + dudy;
+ dvdya = dxdya * dvdx + dvdy;
+
+ xa = x[0] + dy * dxdya + (off_y * dxdya);
+ ua = u[0] + dy * dudya + (off_y * dudya);
+ va = v[0] + dy * dvdya + (off_y * dvdya);
+
+ if (compositing) {
+ if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true);
+ else _rasterMaskedPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, 3);
+ } else if (blending) {
+ _rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity);
+ } else {
+ _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, false);
+ }
+ upper = true;
+ }
+ //Draw lower segment if possibly visible
+ if (yi[1] < yi[2]) {
+ off_y = y[1] < regionTop ? (regionTop - y[1]) : 0;
+ if (!upper) xb += (off_y *dxdyb);
+
+ // Set slopes along left edge and perform subpixel pre-stepping
+ dxdya = dxdy[2];
+ dudya = dxdya * dudx + dudy;
+ dvdya = dxdya * dvdx + dvdy;
+ dy = 1 - (y[1] - yi[1]);
+ xa = x[1] + dy * dxdya + (off_y * dxdya);
+ ua = u[1] + dy * dudya + (off_y * dudya);
+ va = v[1] + dy * dvdya + (off_y * dvdya);
+
+ if (compositing) {
+ if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true);
+ else _rasterMaskedPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, 4);
+ } else if (blending) {
+ _rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity);
+ } else {
+ _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, false);
+ }
+ }
+ }
+}
+
+
+static AASpans* _AASpans(float ymin, float ymax, const SwImage* image, const SwBBox* region)
+{
+ auto yStart = static_cast<int32_t>(ymin);
+ auto yEnd = static_cast<int32_t>(ymax);
+
+ if (!_arrange(image, region, yStart, yEnd)) return nullptr;
+
+ auto aaSpans = static_cast<AASpans*>(malloc(sizeof(AASpans)));
+ aaSpans->yStart = yStart;
+ aaSpans->yEnd = yEnd;
+
+ //Initialize X range
+ auto height = yEnd - yStart;
+
+ aaSpans->lines = static_cast<AALine*>(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->buf32 + (offset + line->x[0]);
+ if (line->x[0] > 1) pixel = *(dst - 1);
+ else pixel = *dst;
+
+ pos = 1;
+ while (pos <= line->length[0]) {
+ *dst = INTERPOLATE(*dst, pixel, line->coverage[0] * pos);
+ ++dst;
+ ++pos;
+ }
+
+ //Right edge
+ dst = surface->buf32 + (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(*dst, pixel, 255 - (line->coverage[1] * (line->length[1] - (width - pos))));
+ --dst;
+ --pos;
+ }
+ }
+ y++;
+ }
+
+ free(aaSpans->lines);
+ free(aaSpans);
+
+ return true;
+}
+
+
+/*
+ 2 triangles constructs 1 mesh.
+ below figure illustrates vert[4] index info.
+ If you need better quality, please divide a mesh by more number of triangles.
+
+ 0 -- 1
+ | / |
+ | / |
+ 3 -- 2
+*/
+static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox* region, uint8_t opacity)
+{
+ if (surface->channelSize == sizeof(uint8_t)) {
+ TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!");
+ return false;
+ }
+
+ //Exceptions: No dedicated drawing area?
+ if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false;
+
+ /* Prepare vertices.
+ shift XY coordinates to match the sub-pixeling technique. */
+ Vertex vertices[4];
+ vertices[0] = {{0.0f, 0.0f}, {0.0f, 0.0f}};
+ vertices[1] = {{float(image->w), 0.0f}, {float(image->w), 0.0f}};
+ vertices[2] = {{float(image->w), float(image->h)}, {float(image->w), float(image->h)}};
+ vertices[3] = {{0.0f, float(image->h)}, {0.0f, float(image->h)}};
+
+ float ys = FLT_MAX, ye = -1.0f;
+ for (int i = 0; i < 4; i++) {
+ mathMultiply(&vertices[i].pt, transform);
+
+ if (vertices[i].pt.y < ys) ys = vertices[i].pt.y;
+ if (vertices[i].pt.y > ye) ye = vertices[i].pt.y;
+ }
+
+ auto aaSpans = _AASpans(ys, ye, image, region);
+ if (!aaSpans) return true;
+
+ Polygon polygon;
+
+ //Draw the first polygon
+ polygon.vertex[0] = vertices[0];
+ polygon.vertex[1] = vertices[1];
+ polygon.vertex[2] = vertices[3];
+
+ _rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity);
+
+ //Draw the second polygon
+ polygon.vertex[0] = vertices[1];
+ polygon.vertex[1] = vertices[2];
+ polygon.vertex[2] = vertices[3];
+
+ _rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity);
+
+#if 0
+ if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) {
+ _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
+ }
+#endif
+ return _apply(surface, aaSpans);
+}
+
+
+/*
+ Provide any number of triangles to draw a mesh using the supplied image.
+ Indexes are not used, so each triangle (Polygon) vertex has to be defined, even if they copy the previous one.
+ Example:
+
+ 0 -- 1 0 -- 1 0
+ | / | --> | / / |
+ | / | | / / |
+ 2 -- 3 2 1 -- 2
+
+ Should provide two Polygons, one for each triangle.
+ // TODO: region?
+*/
+static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox* region, uint8_t opacity)
+{
+ if (surface->channelSize == sizeof(uint8_t)) {
+ TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon mesh!");
+ return false;
+ }
+
+ //Exceptions: No dedicated drawing area?
+ if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false;
+
+ // Step polygons once to transform
+ auto transformedTris = (Polygon*)malloc(sizeof(Polygon) * mesh->triangleCnt);
+ float ys = FLT_MAX, ye = -1.0f;
+ for (uint32_t i = 0; i < mesh->triangleCnt; i++) {
+ transformedTris[i] = mesh->triangles[i];
+ mathMultiply(&transformedTris[i].vertex[0].pt, transform);
+ mathMultiply(&transformedTris[i].vertex[1].pt, transform);
+ mathMultiply(&transformedTris[i].vertex[2].pt, transform);
+
+ if (transformedTris[i].vertex[0].pt.y < ys) ys = transformedTris[i].vertex[0].pt.y;
+ else if (transformedTris[i].vertex[0].pt.y > ye) ye = transformedTris[i].vertex[0].pt.y;
+ if (transformedTris[i].vertex[1].pt.y < ys) ys = transformedTris[i].vertex[1].pt.y;
+ else if (transformedTris[i].vertex[1].pt.y > ye) ye = transformedTris[i].vertex[1].pt.y;
+ if (transformedTris[i].vertex[2].pt.y < ys) ys = transformedTris[i].vertex[2].pt.y;
+ else if (transformedTris[i].vertex[2].pt.y > ye) ye = transformedTris[i].vertex[2].pt.y;
+
+ // Convert normalized UV coordinates to image coordinates
+ transformedTris[i].vertex[0].uv.x *= (float)image->w;
+ transformedTris[i].vertex[0].uv.y *= (float)image->h;
+ transformedTris[i].vertex[1].uv.x *= (float)image->w;
+ transformedTris[i].vertex[1].uv.y *= (float)image->h;
+ transformedTris[i].vertex[2].uv.x *= (float)image->w;
+ transformedTris[i].vertex[2].uv.y *= (float)image->h;
+ }
+
+ // Get AA spans and step polygons again to draw
+ 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);
+ }
+#if 0
+ if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) {
+ _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
+ }
+#endif
+ _apply(surface, aaSpans);
+ }
+ free(transformedTris);
+ return true;
+}