sw_engine texmap: introduced texture mapping polygon drawing.
authorHermet Park <chuneon.park@samsung.com>
Thu, 18 Nov 2021 02:03:29 +0000 (11:03 +0900)
committerJunsuChoi <jsuya.choi@samsung.com>
Thu, 25 Nov 2021 08:56:39 +0000 (17:56 +0900)
Introduced the texture-mapping algorithm for the image-scaler.
With this approach, we can support any arbitrary forms of the transformed shapes
while appling the scale factors - x/y separately.

Also this is more efficient when the transformed image is far from the rectangular form
because our current normal image rasterizer based on the given drawing region whereas
our new approach is span-based.

Thus, we applied this mechanism only for the transformed images.

src/lib/sw_engine/meson.build
src/lib/sw_engine/tvgSwRaster.cpp
src/lib/sw_engine/tvgSwRasterTexmap.h [new file with mode: 0644]

index 0b1a6a7..f390d03 100644 (file)
@@ -3,6 +3,7 @@ source_file = [
    'tvgSwRasterC.h',
    'tvgSwRasterAvx.h',
    'tvgSwRasterNeon.h',
+   'tvgSwRasterTexmap.h',
    'tvgSwFill.cpp',
    'tvgSwImage.cpp',
    'tvgSwMath.cpp',
index e75f260..57adb7c 100644 (file)
@@ -22,9 +22,6 @@
 #include "tvgMath.h"
 #include "tvgRender.h"
 #include "tvgSwCommon.h"
-#include "tvgSwRasterC.h"
-#include "tvgSwRasterAvx.h"
-#include "tvgSwRasterNeon.h"
 
 /************************************************************************/
 /* Internal Class Implementation                                        */
@@ -62,6 +59,18 @@ static inline uint32_t _argbJoin(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
 }
 
 
+static inline uint32_t _interpolate(uint32_t a, uint32_t c0, uint32_t c1)
+{
+    return (((((((c0 >> 8) & 0xff00ff) - ((c1 >> 8) & 0xff00ff)) * a) + (c1 & 0xff00ff00)) & 0xff00ff00) + ((((((c0 & 0xff00ff) - (c1 & 0xff00ff)) * a) >> 8) + (c1 & 0xff00ff)) & 0xff00ff));
+}
+
+
+#include "tvgSwRasterTexmap.h"
+#include "tvgSwRasterC.h"
+#include "tvgSwRasterAvx.h"
+#include "tvgSwRasterNeon.h"
+
+
 static inline bool _compositing(const SwSurface* surface)
 {
     if (!surface->compositor || surface->compositor->method == CompositeMethod::None) return false;
@@ -76,7 +85,6 @@ static inline uint32_t _halfScale(float scale)
     return halfScale;
 }
 
-
 //Bilinear Interpolation
 static uint32_t _interpUpScaler(const uint32_t *img, uint32_t w, uint32_t h, float sx, float sy)
 {
@@ -1054,414 +1062,16 @@ static bool _directRleRGBAImage(SwSurface* surface, const SwImage* image, uint32
 /* Transformed RGBA Image                                               */
 /************************************************************************/
 
-static bool _rasterTransformedMaskedTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t (*blendMethod)(uint32_t))
-{
-    TVGLOG("SW_ENGINE", "Transformed Masked Image");
-
-    auto img = image->data;
-    auto w = image->w;
-    auto h = image->h;
-    auto dbuffer = &surface->buffer[region.min.y * surface->stride + region.min.x];
-    auto cbuffer = &surface->compositor->image.data[region.min.y * surface->compositor->image.stride + region.min.x];
-
-    for (auto y = region.min.y; y < region.max.y; ++y) {
-        auto dst = dbuffer;
-        auto cmp = cbuffer;
-        float ey1 = y * itransform->e12 + itransform->e13;
-        float ey2 = y * itransform->e22 + itransform->e23;
-        for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) {
-            auto rX = static_cast<uint32_t>(roundf(x * itransform->e11 + ey1));
-            auto rY = static_cast<uint32_t>(roundf(x * itransform->e21 + ey2));
-            if (rX >= w || rY >= h) continue;
-            auto src = ALPHA_BLEND(img[rX + (rY * image->stride)], _multiplyAlpha(opacity, blendMethod(*cmp)));
-            *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src));
-        }
-        dbuffer += surface->stride;
-        cbuffer += surface->compositor->image.stride;
-    }
-    return true;
-}
-
-
-static bool _rasterTransformedMaskedRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t (*blendMethod)(uint32_t))
-{
-    TVGLOG("SW_ENGINE", "Transformed Masked Image");
-
-    auto img = image->data;
-    auto w = image->w;
-    auto h = image->h;
-    auto dbuffer = &surface->buffer[region.min.y * surface->stride + region.min.x];
-    auto cbuffer = &surface->compositor->image.data[region.min.y * surface->compositor->image.stride + region.min.x];
-
-    for (auto y = region.min.y; y < region.max.y; ++y) {
-        auto dst = dbuffer;
-        auto cmp = cbuffer;
-        float ey1 = y * itransform->e12 + itransform->e13;
-        float ey2 = y * itransform->e22 + itransform->e23;
-        for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) {
-            auto rX = static_cast<uint32_t>(roundf(x * itransform->e11 + ey1));
-            auto rY = static_cast<uint32_t>(roundf(x * itransform->e21 + ey2));
-            if (rX >= w || rY >= h) continue;
-            auto src = ALPHA_BLEND(img[rX + (rY * image->stride)], blendMethod(*cmp));
-            *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src));
-        }
-        dbuffer += surface->stride;
-        cbuffer += surface->compositor->image.stride;
-    }
-    return true;
-}
-
-
-static bool _rasterTransformedTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity)
-{
-    auto img = image->data;
-    auto w = image->w;
-    auto h = image->h;
-    auto dbuffer = &surface->buffer[region.min.y * surface->stride + region.min.x];
-
-    for (auto y = region.min.y; y < region.max.y; ++y) {
-        auto dst = dbuffer;
-        auto ey1 = y * itransform->e12 + itransform->e13;
-        auto ey2 = y * itransform->e22 + itransform->e23;
-        for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
-            auto rX = static_cast<uint32_t>(roundf(x * itransform->e11 + ey1));
-            auto rY = static_cast<uint32_t>(roundf(x * itransform->e21 + ey2));
-            if (rX >= w || rY >= h) continue;
-
-            auto src = ALPHA_BLEND(img[rX + (rY * image->stride)], opacity);
-            *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src));
-        }
-        dbuffer += surface->stride;
-    }
-    return true;
-}
-
-
-static bool _rasterDownScaledMaskedTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale, uint32_t (*blendMethod)(uint32_t))
-{
-    TVGLOG("SW_ENGINE", "Down Scaled Masked Image");
-
-    auto img = image->data;
-    auto w = image->w;
-    auto h = image->h;
-    auto dbuffer = &surface->buffer[region.min.y * surface->stride + region.min.x];
-    auto cbuffer = &surface->compositor->image.data[region.min.y * surface->compositor->image.stride + region.min.x];
-
-    for (auto y = region.min.y; y < region.max.y; ++y) {
-        auto dst = dbuffer;
-        auto cmp = cbuffer;
-        float ey1 = y * itransform->e12 + itransform->e13;
-        float ey2 = y * itransform->e22 + itransform->e23;
-        for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) {
-            auto rX = static_cast<uint32_t>(roundf(x * itransform->e11 + ey1));
-            auto rY = static_cast<uint32_t>(roundf(x * itransform->e21 + ey2));
-            if (rX >= w || rY >= h) continue;
-            uint32_t src;
-            if (rX < halfScale || rY < halfScale || rX >= w - halfScale || rY >= h - halfScale) {
-                src = ALPHA_BLEND(img[rX + (rY * image->stride)], _multiplyAlpha(opacity, blendMethod(*cmp)));
-            } else {
-                src = ALPHA_BLEND(_interpDownScaler(img, image->stride, h, rX, rY, halfScale), _multiplyAlpha(opacity, blendMethod(*cmp)));
-            }
-            *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src));
-        }
-        dbuffer += surface->stride;
-        cbuffer += surface->compositor->image.stride;
-    }
-    return true;
-}
-
-
-static bool _rasterDownScaledMaskedRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t halfScale, uint32_t (*blendMethod)(uint32_t))
-{
-    TVGLOG("SW_ENGINE", "Down Scaled Masked Image");
-
-    auto img = image->data;
-    auto w = image->w;
-    auto h = image->h;
-    auto dbuffer = &surface->buffer[region.min.y * surface->stride + region.min.x];
-    auto cbuffer = &surface->compositor->image.data[region.min.y * surface->compositor->image.stride + region.min.x];
-
-    for (auto y = region.min.y; y < region.max.y; ++y) {
-        auto dst = dbuffer;
-        auto cmp = cbuffer;
-        float ey1 = y * itransform->e12 + itransform->e13;
-        float ey2 = y * itransform->e22 + itransform->e23;
-        for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) {
-            auto rX = static_cast<uint32_t>(roundf(x * itransform->e11 + ey1));
-            auto rY = static_cast<uint32_t>(roundf(x * itransform->e21 + ey2));
-            if (rX >= w || rY >= h) continue;
-            uint32_t src;
-            if (rX < halfScale || rY < halfScale || rX >= w - halfScale || rY >= h - halfScale) {
-                src = ALPHA_BLEND(img[rX + (rY * image->stride)], blendMethod(*cmp));
-            } else {
-                src = ALPHA_BLEND(_interpDownScaler(img, image->stride, h, rX, rY, halfScale), blendMethod(*cmp));
-            }
-            *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src));
-        }
-        dbuffer += surface->stride;
-        cbuffer += surface->compositor->image.stride;
-    }
-    return true;
-}
-
-
-static bool _rasterDownScaledTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale)
-{
-    auto img = image->data;
-    auto w = image->w;
-    auto h = image->h;
-    auto dbuffer = &surface->buffer[region.min.y * surface->stride + region.min.x];
-
-    for (auto y = region.min.y; y < region.max.y; ++y) {
-        auto dst = dbuffer;
-        auto ey1 = y * itransform->e12 + itransform->e13;
-        auto ey2 = y * itransform->e22 + itransform->e23;
-        for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
-            auto rX = static_cast<uint32_t>(roundf(x * itransform->e11 + ey1));
-            auto rY = static_cast<uint32_t>(roundf(x * itransform->e21 + ey2));
-            if (rX >= w || rY >= h) continue;
-            uint32_t src;
-            if (rX < halfScale || rY < halfScale || rX >= w - halfScale || rY >= h - halfScale) src = ALPHA_BLEND(img[rX + (rY * w)], opacity);
-            else src = ALPHA_BLEND(_interpDownScaler(img, w, h, rX, rY, halfScale), opacity);
-            *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src));
-        }
-        dbuffer += surface->stride;
-    }
-    return true;
-}
-
-
-static bool _rasterUpScaledMaskedTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t (*blendMethod)(uint32_t))
-{
-    TVGLOG("SW_ENGINE", "Up Scaled Masked Image");
-
-    auto img = image->data;
-    auto w = image->w;
-    auto h = image->h;
-    auto dbuffer = &surface->buffer[region.min.y * surface->stride + region.min.x];
-    auto cbuffer = &surface->compositor->image.data[region.min.y * surface->compositor->image.stride + region.min.x];
-
-    for (auto y = region.min.y; y < region.max.y; ++y) {
-        auto dst = dbuffer;
-        auto cmp = cbuffer;
-        float ey1 = y * itransform->e12 + itransform->e13;
-        float ey2 = y * itransform->e22 + itransform->e23;
-        for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) {
-            auto fX = x * itransform->e11 + ey1;
-            auto fY = x * itransform->e21 + ey2;
-            auto rX = static_cast<uint32_t>(roundf(fX));
-            auto rY = static_cast<uint32_t>(roundf(fY));
-            if (rX >= w || rY >= h) continue;
-            uint32_t src;
-            if (rX == w - 1 || rY == h - 1) src = ALPHA_BLEND(img[rX + (rY * image->stride)], _multiplyAlpha(opacity, blendMethod(*cmp)));
-            else src = ALPHA_BLEND(_interpUpScaler(img, image->stride, h, fX, fY), _multiplyAlpha(opacity, blendMethod(*cmp)));
-            *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src));
-        }
-        dbuffer += surface->stride;
-        cbuffer += surface->compositor->image.stride;
-    }
-    return true;
-}
-
-
-static bool _rasterUpScaledMaskedRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t (*blendMethod)(uint32_t))
-{
-    TVGLOG("SW_ENGINE", "Up Scaled Masked Image");
-
-    auto img = image->data;
-    auto w = image->w;
-    auto h = image->h;
-    auto dbuffer = &surface->buffer[region.min.y * surface->stride + region.min.x];
-    auto cbuffer = &surface->compositor->image.data[region.min.y * surface->compositor->image.stride + region.min.x];
-
-    for (auto y = region.min.y; y < region.max.y; ++y) {
-        auto dst = dbuffer;
-        auto cmp = cbuffer;
-        float ey1 = y * itransform->e12 + itransform->e13;
-        float ey2 = y * itransform->e22 + itransform->e23;
-        for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) {
-            auto fX = x * itransform->e11 + ey1;
-            auto fY = x * itransform->e21 + ey2;
-            auto rX = static_cast<uint32_t>(roundf(fX));
-            auto rY = static_cast<uint32_t>(roundf(fY));
-            if (rX >= w || rY >= h) continue;
-            uint32_t src;
-            if (rX == w - 1 || rY == h - 1) src = ALPHA_BLEND(img[rX + (rY * image->stride)], blendMethod(*cmp));
-            else src = ALPHA_BLEND(_interpUpScaler(img, image->stride, h, fX, fY), blendMethod(*cmp));
-            *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src));
-        }
-        dbuffer += surface->stride;
-        cbuffer += surface->compositor->image.stride;
-    }
-    return true;
-}
-
-
-static bool _rasterUpScaledTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity)
-{
-    auto img = image->data;
-    auto w = image->w;
-    auto h = image->h;
-    auto dbuffer = &surface->buffer[region.min.y * surface->stride + region.min.x];
-
-    for (auto y = region.min.y; y < region.max.y; ++y) {
-        auto dst = dbuffer;
-        auto ey1 = y * itransform->e12 + itransform->e13;
-        auto ey2 = y * itransform->e22 + itransform->e23;
-        for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
-            auto fX = x * itransform->e11 + ey1;
-            auto fY = x * itransform->e21 + ey2;
-            auto rX = static_cast<uint32_t>(roundf(fX));
-            auto rY = static_cast<uint32_t>(roundf(fY));
-            if (rX >= w || rY >= h) continue;
-            uint32_t src;
-            if (rX == w - 1 || rY == h - 1) src = ALPHA_BLEND(img[rX + (rY * image->stride)], opacity);
-            else src = ALPHA_BLEND(_interpUpScaler(img, image->stride, h, fX, fY), opacity);
-            *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src));
-        }
-        dbuffer += surface->stride;
-    }
-    return true;
-}
-
-
-static bool _rasterTransformedRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region)
-{
-    auto img = image->data;
-    auto w = image->w;
-    auto h = image->h;
-
-    for (auto y = region.min.y; y < region.max.y; ++y) {
-        auto dst = &surface->buffer[y * surface->stride + region.min.x];
-        auto ey1 = y * itransform->e12 + itransform->e13;
-        auto ey2 = y * itransform->e22 + itransform->e23;
-        for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
-            auto rX = static_cast<uint32_t>(roundf(x * itransform->e11 + ey1));
-            auto rY = static_cast<uint32_t>(roundf(x * itransform->e21 + ey2));
-            if (rX >= w || rY >= h) continue;
-            auto src = img[rX + (rY * image->stride)];
-            *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src));
-        }
-    }
-    return true;
-}
-
-
-static bool _rasterDownScaledRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t halfScale)
-{
-    auto img = image->data;
-    auto w = image->w;
-    auto h = image->h;
-
-    for (auto y = region.min.y; y < region.max.y; ++y) {
-        auto dst = &surface->buffer[y * surface->stride + region.min.x];
-        auto ey1 = y * itransform->e12 + itransform->e13;
-        auto ey2 = y * itransform->e22 + itransform->e23;
-        for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
-            auto rX = static_cast<uint32_t>(roundf(x * itransform->e11 + ey1));
-            auto rY = static_cast<uint32_t>(roundf(x * itransform->e21 + ey2));
-            if (rX >= w || rY >= h) continue;
-            uint32_t src;
-            if (rX < halfScale || rY < halfScale || rX >= w - halfScale || rY >= h - halfScale) src = img[rX + (rY * w)];
-            else src = _interpDownScaler(img, w, h, rX, rY, halfScale);
-            *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src));
-        }
-    }
-    return true;
-}
-
-
-static bool _rasterUpScaledRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region)
-{
-    auto img = image->data;
-    auto w = image->w;
-    auto h = image->h;
-
-    for (auto y = region.min.y; y < region.max.y; ++y) {
-        auto dst = &surface->buffer[y * surface->stride + region.min.x];
-        auto ey1 = y * itransform->e12 + itransform->e13;
-        auto ey2 = y * itransform->e22 + itransform->e23;
-        for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
-            auto fX = x * itransform->e11 + ey1;
-            auto fY = x * itransform->e21 + ey2;
-            auto rX = static_cast<uint32_t>(roundf(fX));
-            auto rY = static_cast<uint32_t>(roundf(fY));
-            if (rX >= w || rY >= h) continue;
-            uint32_t src;
-            if (rX == w - 1 || rY == h - 1) src = img[rX + (rY * w)];
-            else src = _interpUpScaler(img, w, h, fX, fY);
-            *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src));
-        }
-    }
-    return true;
-}
-
-
 static bool _transformedRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint32_t opacity)
 {
-    Matrix itransform;
-    if (transform && !mathInverse(transform, &itransform)) return false;
-
-    auto halfScale = _halfScale(image->scale);
-
     if (_compositing(surface)) {
-        if (opacity == 255) {
-            //Transformd
-            if (mathEqual(image->scale, 1.0f)) {
-                if (surface->compositor->method == CompositeMethod::AlphaMask) {
-                    return _rasterTransformedMaskedRGBAImage(surface, image, &itransform, region, surface->blender.alpha);
-                } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
-                    return _rasterTransformedMaskedRGBAImage(surface, image, &itransform, region, surface->blender.ialpha);
-                }
-            //Transformed + DownScaled
-            } else if (image->scale < DOWN_SCALE_TOLERANCE) {
-                if (surface->compositor->method == CompositeMethod::AlphaMask) {
-                    return _rasterDownScaledMaskedRGBAImage(surface, image, &itransform, region, halfScale, surface->blender.alpha);
-                } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
-                    return _rasterDownScaledMaskedRGBAImage(surface, image, &itransform, region, halfScale, surface->blender.ialpha);
-                }
-            //Transformed + UpScaled
-            } else {
-                if (surface->compositor->method == CompositeMethod::AlphaMask) {
-                    return _rasterUpScaledMaskedRGBAImage(surface, image, &itransform, region, surface->blender.alpha);
-                } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
-                    return _rasterUpScaledMaskedRGBAImage(surface, image, &itransform, region, surface->blender.ialpha);
-                }
-            }
-        } else {
-            //Transformd
-            if (mathEqual(image->scale, 1.0f)) {
-                if (surface->compositor->method == CompositeMethod::AlphaMask) {
-                    return _rasterTransformedMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity, surface->blender.alpha);
-                } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
-                    return _rasterTransformedMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity, surface->blender.ialpha);
-                }
-            //Transformed + DownScaled
-            } else if (image->scale < DOWN_SCALE_TOLERANCE) {
-                if (surface->compositor->method == CompositeMethod::AlphaMask) {
-                    return _rasterDownScaledMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale, surface->blender.alpha);
-                } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
-                    return _rasterDownScaledMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale, surface->blender.ialpha);
-                }
-            //Transformed + UpScaled
-            } else {
-                if (surface->compositor->method == CompositeMethod::AlphaMask) {
-                    return _rasterUpScaledMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity, surface->blender.alpha);
-                } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
-                    return _rasterUpScaledMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity,  surface->blender.ialpha);
-                }
-            }
+        if (surface->compositor->method == CompositeMethod::AlphaMask) {
+            return _rasterTexmapPolygon(surface, image, transform, region, opacity, surface->blender.alpha);
+        } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+            return _rasterTexmapPolygon(surface, image, transform, region, opacity, surface->blender.ialpha);
         }
     } else {
-        if (opacity == 255) {
-            if (mathEqual(image->scale, 1.0f)) return _rasterTransformedRGBAImage(surface, image, &itransform, region);
-            else if (image->scale < DOWN_SCALE_TOLERANCE) return _rasterDownScaledRGBAImage(surface, image, &itransform, region, halfScale);
-            else return _rasterUpScaledRGBAImage(surface, image, &itransform, region);
-        } else {
-            if (mathEqual(image->scale, 1.0f)) return _rasterTransformedTranslucentRGBAImage(surface, image, &itransform, region,  opacity);
-            else if (image->scale < DOWN_SCALE_TOLERANCE) return _rasterDownScaledTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale);
-            else return _rasterUpScaledTranslucentRGBAImage(surface, image, &itransform, region, opacity);
-        }
+        return _rasterTexmapPolygon(surface, image, transform, region, opacity, nullptr);
     }
     return false;
 }
@@ -1882,7 +1492,6 @@ static bool _rasterRGBAImage(SwSurface* surface, SwImage* image, const Matrix* t
     } else {
         if (image->direct) return _directRGBAImage(surface, image, region, opacity);
         else if (image->scaled) return _scaledRGBAImage(surface, image, transform, region, opacity);
-        //OPTIMIZE_ME: Replace with the TexMap Rasterizer
         else return _transformedRGBAImage(surface, image, transform, region, opacity);
     }
 }
diff --git a/src/lib/sw_engine/tvgSwRasterTexmap.h b/src/lib/sw_engine/tvgSwRasterTexmap.h
new file mode 100644 (file)
index 0000000..3ba1e51
--- /dev/null
@@ -0,0 +1,382 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. 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 Vertex
+{
+   Point pt;
+   Point uv;
+};
+
+struct Polygon
+{
+   Vertex vertex[3];
+};
+
+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;
+
+static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox& region, int ystart, int yend, uint32_t opacity, uint32_t (*blendMethod)(uint32_t))
+{
+#define RASTER_COMMON() \
+    uu = (int) u; \
+    vv = (int) v; \
+    /* FIXME: sometimes u and v are < 0 - don'tc crash */ \
+    if (uu < 0) uu = 0;\
+    if (vv < 0) vv = 0; \
+    \
+    /* Range exception handling */ \
+    /* OPTIMIZE ME, handle in advance? */ \
+    if (uu >= sw) uu = sw - 1; \
+    if (vv >= sh) vv = sh - 1; \
+    \
+    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(ar, px, px2); \
+    } \
+    /* 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(ar, px2, px3); \
+        } \
+        px = _interpolate(ab, px, px2); \
+    }
+
+    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->data;
+    auto dbuf = surface->buffer;
+    int sw = static_cast<int>(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;
+    float dx, u, v, iptr;
+    uint32_t* buf;
+
+    //Range exception handling
+    if (ystart >= region.max.y) return;
+    if (ystart < region.min.y) ystart = region.min.y;
+    if (yend > region.max.y) yend = region.max.y;
+
+    //Loop through all lines in the segment
+    y = ystart;
+
+    while (y < yend) {
+        x1 = _xa;
+        x2 = _xb;
+
+        //Range exception handling
+        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;
+
+        //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 (blendMethod) {
+            TVGLOG("SW_ENGINE", "Transformed Masked Image");
+            auto cmp = &surface->compositor->image.data[y * surface->compositor->image.stride + x1];
+            if (opacity == 255) {
+                //Draw horizontal line
+                while (x++ < x2) {
+                    RASTER_COMMON();
+                    auto src = ALPHA_BLEND(px, blendMethod(*cmp));
+                    *buf = src + ALPHA_BLEND(*buf, surface->blender.ialpha(src));
+                    ++cmp;
+                    ++buf;
+                    //Step UV horizontally
+                    u += _dudx;
+                    v += _dvdx;
+                }
+            } else {
+                //Draw horizontal line
+                while (x++ < x2) {
+                    RASTER_COMMON();
+                    auto src = ALPHA_BLEND(px, _multiplyAlpha(opacity, blendMethod(*cmp)));
+                    *buf = src + ALPHA_BLEND(*buf, surface->blender.ialpha(src));
+                    ++buf;
+                    ++cmp;
+                    //Step UV horizontally
+                    u += _dudx;
+                    v += _dvdx;
+                }    
+            }
+        } else {
+            if (opacity == 255) {
+                //Draw horizontal line
+                while (x++ < x2) {
+                    RASTER_COMMON();
+                    *buf = px + ALPHA_BLEND(*buf, surface->blender.ialpha(px));
+                    ++buf;
+                    //Step UV horizontally
+                    u += _dudx;
+                    v += _dvdx;
+                }
+            } else {
+                //Draw horizontal line
+                while (x++ < x2) {
+                    RASTER_COMMON();
+                    auto src = ALPHA_BLEND(px, opacity);
+                    *buf = src + ALPHA_BLEND(*buf, surface->blender.ialpha(src));
+                    ++buf;
+                    //Step UV horizontally
+                    u += _dudx;
+                    v += _dvdx;
+                }
+            }
+        }
+next:
+        //Step along both edges
+        _xa += _dxdya;
+        _xb += _dxdyb;
+        _ua += _dudya;
+        _va += _dvdya;
+
+        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, uint32_t opacity, Polygon& polygon, uint32_t (*blendMethod)(uint32_t))
+{
+    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];
+
+    //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] < region.min.y ? (region.min.y - 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);
+            _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blendMethod);
+            upper = true;
+        }
+        //Draw lower segment if possibly visible
+        if (yi[1] < yi[2]) {
+            off_y = y[1] < region.min.y ? (region.min.y - 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);
+            _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blendMethod);
+        }
+    //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] < region.min.y ? (region.min.y - 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);
+
+            _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blendMethod);
+
+            upper = true;
+        }
+        //Draw lower segment if possibly visible
+        if (yi[1] < yi[2]) {
+            off_y = y[1] < region.min.y ? (region.min.y - 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);
+
+            _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blendMethod);
+        }
+    }
+}
+
+
+/*
+    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, uint32_t opacity, uint32_t (*blendMethod)(uint32_t))
+{
+   /* 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)};
+
+    for (int i = 0; i < 4; i++) mathMultiply(&vertices[i].pt, transform);
+
+    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, opacity, polygon, blendMethod);
+
+    //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);
+    
+    return true;
+}
\ No newline at end of file