From e3acb24cd4d19b5801fd97822201c2a69c2da138 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Fri, 12 Nov 2021 15:03:52 +0900 Subject: [PATCH 01/16] infra bot: Add Memcheck bot (valgrind) If the unit test is successful, valgrind memory check is executed based on the test. If a leak occurs in the test result, the bot notifies the PR as a comment. This notify may not be directly related to the created current PR. (Asan is on hold because it is not well tested in the github action CI environment.) --- .github/workflows/actions.yml | 13 ++++++++++++- .github/workflows/memcheck_valgrind.sh | 27 +++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100755 .github/workflows/memcheck_valgrind.sh diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 69c5bee..d09b12f 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -62,7 +62,9 @@ jobs: - name: Install Packages run: | sudo apt-get update - sudo apt-get install ninja-build gcc-multilib g++-multilib libgtest-dev meson cmake cmake-data + sudo apt-get install ninja-build gcc-multilib g++-multilib libgtest-dev meson cmake cmake-data libasan5 valgrind + sudo apt-get install curl jq + sudo apt-get install software-properties-common - name: Install TurboJPEG library run: sudo apt-get install libturbojpeg0-dev libjpeg8-dev @@ -85,3 +87,12 @@ jobs: with: name: UnitTestReport path: build/meson-logs/testlog.txt + + - name: Run memcheck Script(valgrind) + run: | + export PATH=$PATH:~/.local/bin/ + chmod +x "${GITHUB_WORKSPACE}/.github/workflows/memcheck_valgrind.sh" + "${GITHUB_WORKSPACE}/.github/workflows/memcheck_valgrind.sh" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + diff --git a/.github/workflows/memcheck_valgrind.sh b/.github/workflows/memcheck_valgrind.sh new file mode 100755 index 0000000..3dd59c8 --- /dev/null +++ b/.github/workflows/memcheck_valgrind.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +echo "Run Valgrind" +echo "valgrind --leak-check=yes ./tvgUnitTests" +cd ./build/test + +valgrind --leak-check=yes ./tvgUnitTests > memcheck_valgrind.txt 2>&1 + + +PAYLOAD_MEMCHECK=`cat memcheck_valgrind.txt` +COMMENTS_URL=$(cat $GITHUB_EVENT_PATH | jq -r .pull_request.comments_url) + +echo $COMMENTS_URL +echo "MEMCHECK errors:" +echo $PAYLOAD_MEMCHECK + +if [[ $PAYLOAD_MEMCHECK == *"definitely lost:"* || $PAYLOAD_MEMCHECK == *"Invalid read "* || $PAYLOAD_MEMCHECK == *"Invalid write "* ]]; then + OUTPUT+=$'\n**MEMCHECK(VALGRIND) RESULT**:\n' + OUTPUT+=$'\n`valgrind --leak-check=yes ./tvgUnitTests`\n' + OUTPUT+=$'\n```\n' + OUTPUT+="$PAYLOAD_MEMCHECK" + OUTPUT+=$'\n```\n' +fi + +PAYLOAD=$(echo '{}' | jq --arg body "$OUTPUT" '.body = $body') + +curl -s -S -H "Authorization: token $GITHUB_TOKEN" --header "Content-Type: application/vnd.github.VERSION.text+json" --data "$PAYLOAD" "$COMMENTS_URL" -- 2.7.4 From 242ccefe37de670d9d8c5a5b29c29bd2cf739838 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Fri, 12 Nov 2021 14:55:04 +0900 Subject: [PATCH 02/16] sw_engine image: optimized image rendering. Applied the fast-track routine for axis-aligned images. This helps to remove outline generation if no clips. --- src/lib/sw_engine/tvgSwCommon.h | 3 ++- src/lib/sw_engine/tvgSwImage.cpp | 17 +++++++++++++---- src/lib/sw_engine/tvgSwMath.cpp | 34 +++++++++++++++++++--------------- src/lib/sw_engine/tvgSwRenderer.cpp | 7 ++++--- src/lib/tvgMath.h | 6 ++++++ 5 files changed, 44 insertions(+), 23 deletions(-) diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index 31b6e3b..3dad781 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -300,6 +300,7 @@ bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, Sw SwFixed mathMean(SwFixed angle1, SwFixed angle2); SwPoint mathTransform(const Point* to, const Matrix* transform); bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack); +bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee); void shapeReset(SwShape* shape); bool shapePrepare(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite); @@ -322,7 +323,7 @@ bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline); SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid); void strokeFree(SwStroke* stroke); -bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid); +bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool outline); bool imageGenRle(SwImage* image, TVG_UNUSED const Picture* pdata, const SwBBox& renderRegion, bool antiAlias); void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid); void imageReset(SwImage* image); diff --git a/src/lib/sw_engine/tvgSwImage.cpp b/src/lib/sw_engine/tvgSwImage.cpp index 2ef024d..2233f7e 100644 --- a/src/lib/sw_engine/tvgSwImage.cpp +++ b/src/lib/sw_engine/tvgSwImage.cpp @@ -19,7 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#include +#include "tvgMath.h" #include "tvgSwCommon.h" /************************************************************************/ @@ -72,10 +72,19 @@ static bool _genOutline(SwImage* image, const Matrix* transform, SwMpool* mpool, /************************************************************************/ -bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid) +bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool outline) { - if (!_genOutline(image, transform, mpool, tid)) return false; - return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, false); + if (outline || mathRotated(transform)) { + if (!_genOutline(image, transform, mpool, tid)) return false; + return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, false); + //Fast Track, don't need outlines. + } else { + renderRegion.min.x = static_cast(round(transform->e13)); + renderRegion.max.x = renderRegion.min.x + static_cast(image->w); + renderRegion.min.y = static_cast(round(transform->e23)); + renderRegion.max.y= renderRegion.min.y + static_cast(image->h); + return mathClipBBox(clipRegion, renderRegion); + } } diff --git a/src/lib/sw_engine/tvgSwMath.cpp b/src/lib/sw_engine/tvgSwMath.cpp index 22bc97f..e7470c7 100644 --- a/src/lib/sw_engine/tvgSwMath.cpp +++ b/src/lib/sw_engine/tvgSwMath.cpp @@ -442,6 +442,24 @@ SwPoint mathTransform(const Point* to, const Matrix* transform) } +bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee) +{ + clipee.max.x = (clipee.max.x < clipper.max.x) ? clipee.max.x : clipper.max.x; + clipee.max.y = (clipee.max.y < clipper.max.y) ? clipee.max.y : clipper.max.y; + clipee.min.x = (clipee.min.x > clipper.min.x) ? clipee.min.x : clipper.min.x; + clipee.min.y = (clipee.min.y > clipper.min.y) ? clipee.min.y : clipper.min.y; + + //Check valid region + if (clipee.max.x - clipee.min.x < 1 && clipee.max.y - clipee.min.y < 1) return false; + + //Check boundary + if (clipee.min.x >= clipper.max.x || clipee.min.y >= clipper.max.y || + clipee.max.x <= clipper.min.x || clipee.max.y <= clipper.min.y) return false; + + return true; +} + + bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack) { if (!outline) return false; @@ -466,7 +484,6 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, S if (yMin > pt->y) yMin = pt->y; if (yMax < pt->y) yMax = pt->y; } - //Since no antialiasing is applied in the Fast Track case, //the rasterization region has to be rearranged. //https://github.com/Samsung/thorvg/issues/916 @@ -481,18 +498,5 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, S renderRegion.min.y = yMin >> 6; renderRegion.max.y = (yMax + 63) >> 6; } - - renderRegion.max.x = (renderRegion.max.x < clipRegion.max.x) ? renderRegion.max.x : clipRegion.max.x; - renderRegion.max.y = (renderRegion.max.y < clipRegion.max.y) ? renderRegion.max.y : clipRegion.max.y; - renderRegion.min.x = (renderRegion.min.x > clipRegion.min.x) ? renderRegion.min.x : clipRegion.min.x; - renderRegion.min.y = (renderRegion.min.y > clipRegion.min.y) ? renderRegion.min.y : clipRegion.min.y; - - //Check valid region - if (renderRegion.max.x - renderRegion.min.x < 1 && renderRegion.max.y - renderRegion.min.y < 1) return false; - - //Check boundary - if (renderRegion.min.x >= clipRegion.max.x || renderRegion.min.y >= clipRegion.max.y || - renderRegion.max.x <= clipRegion.min.x || renderRegion.max.y <= clipRegion.min.y) return false; - - return true; + return mathClipBBox(clipRegion, renderRegion); } \ No newline at end of file diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index b50aa62..8cc0b8e 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -192,10 +192,11 @@ struct SwImageTask : SwTask if (!image.data || image.w == 0 || image.h == 0) goto end; image.stride = image.w; //same, pixel buffer size. - if (!imagePrepare(&image, transform, clipRegion, bbox, mpool, tid)) goto end; + auto clipPath = (clips.count > 0) ? true : false; - //Clip Path? - if (clips.count > 0) { + if (!imagePrepare(&image, transform, clipRegion, bbox, mpool, tid, clipPath)) goto end; + + if (clipPath) { if (!imageGenRle(&image, pdata, bbox, false)) goto end; if (image.rle) { for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) { diff --git a/src/lib/tvgMath.h b/src/lib/tvgMath.h index 363fd30..b9a0488 100644 --- a/src/lib/tvgMath.h +++ b/src/lib/tvgMath.h @@ -28,6 +28,12 @@ #include #include "tvgCommon.h" +static inline bool mathRotated(const Matrix* m) +{ + if (fabs(m->e12) > FLT_EPSILON || fabs(m->e21 > FLT_EPSILON)) return true; + else return false; +} + static inline bool mathIdentity(const Matrix* m) { -- 2.7.4 From 4ea1e54c6c8c9bbd0b14ac3b452ba58c32680797 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Fri, 12 Nov 2021 16:37:10 +0900 Subject: [PATCH 03/16] common paint: fix invalid memory access in unit test casting the paint to shape is not allowed if the compositor target is not shape, here it concretely checking the the type before casting.... --- src/lib/tvgPaint.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/tvgPaint.cpp b/src/lib/tvgPaint.cpp index a8f6e5e..f20c920 100644 --- a/src/lib/tvgPaint.cpp +++ b/src/lib/tvgPaint.cpp @@ -206,7 +206,7 @@ void* Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pTransf we can avoid regular ClipPath / AlphaMasking sequence but use viewport for performance */ auto tryFastTrack = false; if (cmpMethod == CompositeMethod::ClipPath) tryFastTrack = true; - else if (cmpMethod == CompositeMethod::AlphaMask) { + else if (cmpMethod == CompositeMethod::AlphaMask && cmpTarget->identifier() == TVG_CLASS_ID_SHAPE) { auto shape = static_cast(cmpTarget); uint8_t a; shape->fillColor(nullptr, nullptr, nullptr, &a); -- 2.7.4 From a04bbdf95d5114a7807db49976d7c4d290aadb39 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Fri, 12 Nov 2021 16:35:07 +0900 Subject: [PATCH 04/16] infra bot: Hotfix memcheck bot for push event --- .github/workflows/memcheck_valgrind.sh | 44 ++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/.github/workflows/memcheck_valgrind.sh b/.github/workflows/memcheck_valgrind.sh index 3dd59c8..9bd8152 100755 --- a/.github/workflows/memcheck_valgrind.sh +++ b/.github/workflows/memcheck_valgrind.sh @@ -1,27 +1,35 @@ #!/bin/bash -echo "Run Valgrind" -echo "valgrind --leak-check=yes ./tvgUnitTests" -cd ./build/test +if [[ -z "$GITHUB_TOKEN" ]]; then + echo "The GITHUB_TOKEN is required." + exit 1 +fi -valgrind --leak-check=yes ./tvgUnitTests > memcheck_valgrind.txt 2>&1 +if [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then + echo "Run Valgrind" + echo "valgrind --leak-check=yes ./tvgUnitTests" + cd ./build/test + valgrind --leak-check=yes ./tvgUnitTests > memcheck_valgrind.txt 2>&1 -PAYLOAD_MEMCHECK=`cat memcheck_valgrind.txt` -COMMENTS_URL=$(cat $GITHUB_EVENT_PATH | jq -r .pull_request.comments_url) -echo $COMMENTS_URL -echo "MEMCHECK errors:" -echo $PAYLOAD_MEMCHECK + PAYLOAD_MEMCHECK=`cat memcheck_valgrind.txt` + COMMENTS_URL=$(cat $GITHUB_EVENT_PATH | jq -r .pull_request.comments_url) -if [[ $PAYLOAD_MEMCHECK == *"definitely lost:"* || $PAYLOAD_MEMCHECK == *"Invalid read "* || $PAYLOAD_MEMCHECK == *"Invalid write "* ]]; then - OUTPUT+=$'\n**MEMCHECK(VALGRIND) RESULT**:\n' - OUTPUT+=$'\n`valgrind --leak-check=yes ./tvgUnitTests`\n' - OUTPUT+=$'\n```\n' - OUTPUT+="$PAYLOAD_MEMCHECK" - OUTPUT+=$'\n```\n' -fi + echo $COMMENTS_URL + echo "MEMCHECK errors:" + echo $PAYLOAD_MEMCHECK + + if [[ $PAYLOAD_MEMCHECK == *"definitely lost:"* || $PAYLOAD_MEMCHECK == *"Invalid read "* || $PAYLOAD_MEMCHECK == *"Invalid write "* ]]; then + OUTPUT+=$'\n**MEMCHECK(VALGRIND) RESULT**:\n' + OUTPUT+=$'\n`valgrind --leak-check=yes ./tvgUnitTests`\n' + OUTPUT+=$'\n```\n' + OUTPUT+="$PAYLOAD_MEMCHECK" + OUTPUT+=$'\n```\n' + fi -PAYLOAD=$(echo '{}' | jq --arg body "$OUTPUT" '.body = $body') + PAYLOAD=$(echo '{}' | jq --arg body "$OUTPUT" '.body = $body') + + curl -s -S -H "Authorization: token $GITHUB_TOKEN" --header "Content-Type: application/vnd.github.VERSION.text+json" --data "$PAYLOAD" "$COMMENTS_URL" +fi -curl -s -S -H "Authorization: token $GITHUB_TOKEN" --header "Content-Type: application/vnd.github.VERSION.text+json" --data "$PAYLOAD" "$COMMENTS_URL" -- 2.7.4 From 1000b7bf11a7afbeacd7c399425d452113993755 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Fri, 12 Nov 2021 17:09:40 +0900 Subject: [PATCH 05/16] common paint: improve rotation condition precision --- src/lib/tvgMath.h | 3 ++- src/lib/tvgPaint.cpp | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/lib/tvgMath.h b/src/lib/tvgMath.h index b9a0488..81c9744 100644 --- a/src/lib/tvgMath.h +++ b/src/lib/tvgMath.h @@ -30,7 +30,8 @@ static inline bool mathRotated(const Matrix* m) { - if (fabs(m->e12) > FLT_EPSILON || fabs(m->e21 > FLT_EPSILON)) return true; + if (fabs(m->e12) > FLT_EPSILON || fabs(m->e21 > FLT_EPSILON) || (m->e11 < -FLT_EPSILON) || (m->e22 < -FLT_EPSILON)) return true; + else return false; } diff --git a/src/lib/tvgPaint.cpp b/src/lib/tvgPaint.cpp index f20c920..b437ccd 100644 --- a/src/lib/tvgPaint.cpp +++ b/src/lib/tvgPaint.cpp @@ -46,10 +46,10 @@ static bool _compFastTrack(Paint* cmpTarget, const RenderTransform* pTransform, if (rTransform) rTransform->update(); //No Rotation? - if (pTransform && (pTransform->m.e12 != 0 || pTransform->m.e21 != 0 || pTransform->m.e11 != pTransform->m.e22)) return false; - if (rTransform && (rTransform->m.e12 != 0 || rTransform->m.e21 != 0 || rTransform->m.e11 != rTransform->m.e22)) return false; + if (pTransform && (fabs(pTransform->m.e12) > FLT_EPSILON || fabs(pTransform->m.e21 > FLT_EPSILON))) return false; + if (rTransform && (fabs(rTransform->m.e12) > FLT_EPSILON || fabs(rTransform->m.e21 > FLT_EPSILON))) return false; - //Othogonal Rectangle? + //Axis-Aligned Rectangle? auto pt1 = pts + 0; auto pt2 = pts + 1; auto pt3 = pts + 2; -- 2.7.4 From 17da31ee22a6161873ac31e746b1a21e7bb8796a Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Fri, 12 Nov 2021 19:06:41 +0900 Subject: [PATCH 06/16] common paint: improve rotation condition precision cover the four cases: 0, 90, 180, 270 degrees... --- src/lib/sw_engine/tvgSwImage.cpp | 29 ++++++++++++++++++++++++----- src/lib/sw_engine/tvgSwMath.cpp | 2 +- src/lib/tvgMath.h | 8 ++++---- src/lib/tvgPaint.cpp | 6 +++--- 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/lib/sw_engine/tvgSwImage.cpp b/src/lib/sw_engine/tvgSwImage.cpp index 2233f7e..b19078e 100644 --- a/src/lib/sw_engine/tvgSwImage.cpp +++ b/src/lib/sw_engine/tvgSwImage.cpp @@ -74,15 +74,34 @@ static bool _genOutline(SwImage* image, const Matrix* transform, SwMpool* mpool, bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool outline) { - if (outline || mathRotated(transform)) { + if (outline || !mathRightAngle(transform)) { if (!_genOutline(image, transform, mpool, tid)) return false; return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, false); //Fast Track, don't need outlines. } else { - renderRegion.min.x = static_cast(round(transform->e13)); - renderRegion.max.x = renderRegion.min.x + static_cast(image->w); - renderRegion.min.y = static_cast(round(transform->e23)); - renderRegion.max.y= renderRegion.min.y + static_cast(image->h); + auto w = static_cast(image->w); + auto h = static_cast(image->h); + + Point pt[4] = {{0 ,0}, {w, 0}, {w, h}, {0, h}}; + for (int i = 0; i < 4; i++) mathMultiply(&pt[i], transform); + + auto xMin = pt[0].x; + auto xMax = pt[0].x; + auto yMin = pt[0].y; + auto yMax = pt[0].y; + + for (uint32_t i = 1; i < 4; ++i) { + if (xMin > pt[i].x) xMin = pt[i].x; + if (xMax < pt[i].x) xMax = pt[i].x; + if (yMin > pt[i].y) yMin = pt[i].y; + if (yMax < pt[i].y) yMax = pt[i].y; + } + + renderRegion.min.x = static_cast(xMin); + renderRegion.max.x = static_cast(round(xMax)); + renderRegion.min.y = static_cast(yMin); + renderRegion.max.y = static_cast(round(yMax)); + return mathClipBBox(clipRegion, renderRegion); } } diff --git a/src/lib/sw_engine/tvgSwMath.cpp b/src/lib/sw_engine/tvgSwMath.cpp index e7470c7..7a3529b 100644 --- a/src/lib/sw_engine/tvgSwMath.cpp +++ b/src/lib/sw_engine/tvgSwMath.cpp @@ -478,7 +478,7 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, S ++pt; - for(uint32_t i = 1; i < outline->ptsCnt; ++i, ++pt) { + for (uint32_t i = 1; i < outline->ptsCnt; ++i, ++pt) { if (xMin > pt->x) xMin = pt->x; if (xMax < pt->x) xMax = pt->x; if (yMin > pt->y) yMin = pt->y; diff --git a/src/lib/tvgMath.h b/src/lib/tvgMath.h index 81c9744..2a3d40b 100644 --- a/src/lib/tvgMath.h +++ b/src/lib/tvgMath.h @@ -28,11 +28,11 @@ #include #include "tvgCommon.h" -static inline bool mathRotated(const Matrix* m) +static inline bool mathRightAngle(const Matrix* m) { - if (fabs(m->e12) > FLT_EPSILON || fabs(m->e21 > FLT_EPSILON) || (m->e11 < -FLT_EPSILON) || (m->e22 < -FLT_EPSILON)) return true; - - else return false; + auto radian = fabsf(atan2(m->e21, m->e11)); + if (radian < FLT_EPSILON || fabsf(radian - float(M_PI_2)) < FLT_EPSILON || fabsf(radian - float(M_PI)) < FLT_EPSILON) return true; + return false; } diff --git a/src/lib/tvgPaint.cpp b/src/lib/tvgPaint.cpp index b437ccd..16e9ab0 100644 --- a/src/lib/tvgPaint.cpp +++ b/src/lib/tvgPaint.cpp @@ -46,10 +46,10 @@ static bool _compFastTrack(Paint* cmpTarget, const RenderTransform* pTransform, if (rTransform) rTransform->update(); //No Rotation? - if (pTransform && (fabs(pTransform->m.e12) > FLT_EPSILON || fabs(pTransform->m.e21 > FLT_EPSILON))) return false; - if (rTransform && (fabs(rTransform->m.e12) > FLT_EPSILON || fabs(rTransform->m.e21 > FLT_EPSILON))) return false; + if (pTransform && !mathRightAngle(&pTransform->m)) return false; + if (rTransform && !mathRightAngle(&rTransform->m)) return false; - //Axis-Aligned Rectangle? + //Perpendicular Rectangle? auto pt1 = pts + 0; auto pt2 = pts + 1; auto pt3 = pts + 2; -- 2.7.4 From 5c1aee1ec82822dea63b79385c650b6096eaba05 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Sun, 14 Nov 2021 00:30:18 +0100 Subject: [PATCH 07/16] sw_engine: avxRasterTranslucentRle implemented --- src/lib/sw_engine/tvgSwRaster.cpp | 6 +++-- src/lib/sw_engine/tvgSwRasterAvx.h | 52 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/lib/sw_engine/tvgSwRaster.cpp b/src/lib/sw_engine/tvgSwRaster.cpp index b5866b2..28c6235 100644 --- a/src/lib/sw_engine/tvgSwRaster.cpp +++ b/src/lib/sw_engine/tvgSwRaster.cpp @@ -200,7 +200,9 @@ static bool _rasterTranslucentRle(SwSurface* surface, SwRleData* rle, uint32_t c } } -#if defined(THORVG_NEON_VECTOR_SUPPORT) +#if defined(THORVG_AVX_VECTOR_SUPPORT) + return avxRasterTranslucentRle(surface, rle, color); +#elif defined(THORVG_NEON_VECTOR_SUPPORT) return neonRasterTranslucentRle(surface, rle, color); #else return cRasterTranslucentRle(surface, rle, color); @@ -1586,4 +1588,4 @@ bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, co return _rasterImage(surface, image, bbox); } } -} \ No newline at end of file +} diff --git a/src/lib/sw_engine/tvgSwRasterAvx.h b/src/lib/sw_engine/tvgSwRasterAvx.h index 48d34f6..c14a213 100644 --- a/src/lib/sw_engine/tvgSwRasterAvx.h +++ b/src/lib/sw_engine/tvgSwRasterAvx.h @@ -124,4 +124,56 @@ static inline bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& re return true; } + +static inline bool avxRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint32_t color) +{ + auto span = rle->spans; + uint32_t src; + + for (uint32_t i = 0; i < rle->size; ++i) { + auto dst = &surface->buffer[span->y * surface->stride + span->x]; + + if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage); + else src = color; + + auto ialpha = 255 - static_cast(surface->blender.alpha(src)); + + //1. fill the not aligned memory (for 128-bit registers a 16-bytes alignment is required) + auto notAligned = ((uintptr_t)dst & 0xf) / 4; + if (notAligned) { + notAligned = (N_32BITS_IN_128REG - notAligned > span->len ? span->len : N_32BITS_IN_128REG - notAligned); + for (uint32_t x = 0; x < notAligned; ++x, ++dst) { + *dst = src + ALPHA_BLEND(*dst, ialpha); + } + } + + //2. fill the aligned memory using avx - N_32BITS_IN_128REG pixels processed at once + //In order to avoid unneccessary avx variables declarations a check is made whether there are any iterations at all + uint32_t iterations = (span->len - notAligned) / N_32BITS_IN_128REG; + uint32_t avxFilled = 0; + if (iterations > 0) { + auto avxSrc = _mm_set1_epi32(src); + auto avxIalpha = _mm_set1_epi8(ialpha); + + avxFilled = iterations * N_32BITS_IN_128REG; + auto avxDst = (__m128i*)dst; + for (uint32_t x = 0; x < iterations; ++x, ++avxDst) { + *avxDst = _mm_add_epi32(avxSrc, ALPHA_BLEND(*avxDst, avxIalpha)); + } + } + + //3. fill the remaining pixels + int32_t leftovers = span->len - notAligned - avxFilled; + dst += avxFilled; + while (leftovers--) { + *dst = src + ALPHA_BLEND(*dst, ialpha); + dst++; + } + + ++span; + } + return true; +} + + #endif -- 2.7.4 From 3b76f89f133a5b2c7c883e0e8b2fdcc554430887 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Mon, 15 Nov 2021 11:26:31 +0900 Subject: [PATCH 08/16] sw_engine image: code refactoring revert d520da2db8966db19ae83897549a107c01c475c1 After considered the scneario seriously, this doesn't helpful for the performance at all... just increased code complexity. earlier bad decision... revert it. --- src/lib/sw_engine/tvgSwCommon.h | 2 +- src/lib/sw_engine/tvgSwImage.cpp | 34 +++------------------------------- src/lib/sw_engine/tvgSwRenderer.cpp | 6 ++---- 3 files changed, 6 insertions(+), 36 deletions(-) diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index 3dad781..365e9b4 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -323,7 +323,7 @@ bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline); SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid); void strokeFree(SwStroke* stroke); -bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool outline); +bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid); bool imageGenRle(SwImage* image, TVG_UNUSED const Picture* pdata, const SwBBox& renderRegion, bool antiAlias); void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid); void imageReset(SwImage* image); diff --git a/src/lib/sw_engine/tvgSwImage.cpp b/src/lib/sw_engine/tvgSwImage.cpp index b19078e..5167067 100644 --- a/src/lib/sw_engine/tvgSwImage.cpp +++ b/src/lib/sw_engine/tvgSwImage.cpp @@ -72,38 +72,10 @@ static bool _genOutline(SwImage* image, const Matrix* transform, SwMpool* mpool, /************************************************************************/ -bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool outline) +bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid) { - if (outline || !mathRightAngle(transform)) { - if (!_genOutline(image, transform, mpool, tid)) return false; - return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, false); - //Fast Track, don't need outlines. - } else { - auto w = static_cast(image->w); - auto h = static_cast(image->h); - - Point pt[4] = {{0 ,0}, {w, 0}, {w, h}, {0, h}}; - for (int i = 0; i < 4; i++) mathMultiply(&pt[i], transform); - - auto xMin = pt[0].x; - auto xMax = pt[0].x; - auto yMin = pt[0].y; - auto yMax = pt[0].y; - - for (uint32_t i = 1; i < 4; ++i) { - if (xMin > pt[i].x) xMin = pt[i].x; - if (xMax < pt[i].x) xMax = pt[i].x; - if (yMin > pt[i].y) yMin = pt[i].y; - if (yMax < pt[i].y) yMax = pt[i].y; - } - - renderRegion.min.x = static_cast(xMin); - renderRegion.max.x = static_cast(round(xMax)); - renderRegion.min.y = static_cast(yMin); - renderRegion.max.y = static_cast(round(yMax)); - - return mathClipBBox(clipRegion, renderRegion); - } + if (!_genOutline(image, transform, mpool, tid)) return false; + return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, false); } diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index 8cc0b8e..bc63335 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -192,11 +192,9 @@ struct SwImageTask : SwTask if (!image.data || image.w == 0 || image.h == 0) goto end; image.stride = image.w; //same, pixel buffer size. - auto clipPath = (clips.count > 0) ? true : false; + if (!imagePrepare(&image, transform, clipRegion, bbox, mpool, tid)) goto end; - if (!imagePrepare(&image, transform, clipRegion, bbox, mpool, tid, clipPath)) goto end; - - if (clipPath) { + if (clips.count > 0) { if (!imageGenRle(&image, pdata, bbox, false)) goto end; if (image.rle) { for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) { -- 2.7.4 From 9e7abd1a3275a040a5e2f3b1461928da7839c3d3 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Mon, 15 Nov 2021 12:17:36 +0900 Subject: [PATCH 09/16] common math: code refactoring introduced mathZero(), mathEqual() for floating variables. --- src/lib/sw_engine/tvgSwFill.cpp | 2 +- src/lib/sw_engine/tvgSwRaster.cpp | 8 ++++---- src/lib/tvgMath.h | 23 ++++++++++++++++++----- src/lib/tvgPaint.cpp | 21 ++++++++------------- src/lib/tvgRender.cpp | 7 ++----- src/lib/tvgShape.cpp | 9 +++------ src/loaders/svg/tvgSvgLoader.cpp | 4 ++-- src/loaders/svg/tvgSvgSceneBuilder.cpp | 8 ++++---- src/savers/tvg/tvgTvgSaver.cpp | 16 ++++++++-------- 9 files changed, 50 insertions(+), 48 deletions(-) diff --git a/src/lib/sw_engine/tvgSwFill.cpp b/src/lib/sw_engine/tvgSwFill.cpp index 9235242..8afc2c0 100644 --- a/src/lib/sw_engine/tvgSwFill.cpp +++ b/src/lib/sw_engine/tvgSwFill.cpp @@ -260,7 +260,7 @@ void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1); float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1); - if (fabsf(inc) < FLT_EPSILON) { + if (mathZero(inc)) { auto color = _fixedPixel(fill, static_cast(t * FIXPT_SIZE)); rasterRGBA32(dst, color, 0, len); return; diff --git a/src/lib/sw_engine/tvgSwRaster.cpp b/src/lib/sw_engine/tvgSwRaster.cpp index 28c6235..e3640bd 100644 --- a/src/lib/sw_engine/tvgSwRaster.cpp +++ b/src/lib/sw_engine/tvgSwRaster.cpp @@ -1555,11 +1555,11 @@ bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, co if (image->rle) { if (transformed) { if (translucent) { - if (fabsf(scale - 1.0f) <= FLT_EPSILON) return _rasterTranslucentImageRle(surface, image, opacity, &itransform); + if (mathEqual(scale, 1.0f)) return _rasterTranslucentImageRle(surface, image, opacity, &itransform); else if (scale < downScaleTolerance) return _rasterTranslucentDownScaleImageRle(surface, image, opacity, &itransform, halfScale); else return _rasterTranslucentUpScaleImageRle(surface, image, opacity, &itransform); } else { - if (fabsf(scale - 1.0f) <= FLT_EPSILON) return _rasterImageRle(surface, image, &itransform); + if (mathEqual(scale, 1.0f)) return _rasterImageRle(surface, image, &itransform); else if (scale < downScaleTolerance) return _rasterDownScaleImageRle(surface, image, &itransform, halfScale); else return _rasterUpScaleImageRle(surface, image, &itransform); } @@ -1573,11 +1573,11 @@ bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, co } else { if (transformed) { if (translucent) { - if (fabsf(scale - 1.0f) <= FLT_EPSILON) return _rasterTranslucentImage(surface, image, opacity, bbox, &itransform); + if (mathEqual(scale, 1.0f)) return _rasterTranslucentImage(surface, image, opacity, bbox, &itransform); else if (scale < downScaleTolerance) return _rasterTranslucentDownScaleImage(surface, image, opacity, bbox, &itransform, halfScale); else return _rasterTranslucentUpScaleImage(surface, image, opacity, bbox, &itransform); } else { - if (fabsf(scale - 1.0f) <= FLT_EPSILON) return _rasterImage(surface, image, bbox, &itransform); + if (mathEqual(scale, 1.0f)) return _rasterImage(surface, image, bbox, &itransform); else if (scale < downScaleTolerance) return _rasterDownScaleImage(surface, image, bbox, &itransform, halfScale); else return _rasterUpScaleImage(surface, image, bbox, &itransform); } diff --git a/src/lib/tvgMath.h b/src/lib/tvgMath.h index 2a3d40b..9e5c915 100644 --- a/src/lib/tvgMath.h +++ b/src/lib/tvgMath.h @@ -28,19 +28,32 @@ #include #include "tvgCommon.h" + +static inline bool mathZero(float a) +{ + return (fabsf(a) < FLT_EPSILON) ? true : false; +} + + +static inline bool mathEqual(float a, float b) +{ + return (fabsf(a - b) < FLT_EPSILON); +} + + static inline bool mathRightAngle(const Matrix* m) { auto radian = fabsf(atan2(m->e21, m->e11)); - if (radian < FLT_EPSILON || fabsf(radian - float(M_PI_2)) < FLT_EPSILON || fabsf(radian - float(M_PI)) < FLT_EPSILON) return true; + if (radian < FLT_EPSILON || mathEqual(radian, float(M_PI_2)) || mathEqual(radian, float(M_PI))) return true; return false; } static inline bool mathIdentity(const Matrix* m) { - if (fabs(m->e11 - 1) > FLT_EPSILON || fabs(m->e12) > FLT_EPSILON || fabs(m->e13) > FLT_EPSILON || - fabs(m->e21) > FLT_EPSILON || fabs(m->e22 - 1) > FLT_EPSILON || fabs(m->e23) > FLT_EPSILON || - fabs(m->e31) > FLT_EPSILON || fabs(m->e32) > FLT_EPSILON || fabs(m->e33 - 1) > FLT_EPSILON) { + if (!mathEqual(m->e11, 1.0f) || !mathZero(m->e12) || !mathZero(m->e13) || + !mathZero(m->e21) || !mathEqual(m->e22, 1.0f) || !mathZero(m->e23) || + !mathZero(m->e31) || !mathZero(m->e32) || !mathEqual(m->e33, 1.0f)) { return false; } return true; @@ -53,7 +66,7 @@ static inline bool mathInverse(const Matrix* m, Matrix* out) m->e12 * (m->e21 * m->e33 - m->e23 * m->e31) + m->e13 * (m->e21 * m->e32 - m->e22 * m->e31); - if (fabsf(det) < FLT_EPSILON) return false; + if (mathZero(det)) return false; auto invDet = 1 / det; diff --git a/src/lib/tvgPaint.cpp b/src/lib/tvgPaint.cpp index 16e9ab0..e3d2342 100644 --- a/src/lib/tvgPaint.cpp +++ b/src/lib/tvgPaint.cpp @@ -28,11 +28,6 @@ /* Internal Class Implementation */ /************************************************************************/ -static inline bool FLT_SAME(float a, float b) -{ - return (fabsf(a - b) < FLT_EPSILON); -} - static bool _compFastTrack(Paint* cmpTarget, const RenderTransform* pTransform, RenderTransform* rTransform, RenderRegion& viewport) { @@ -55,8 +50,8 @@ static bool _compFastTrack(Paint* cmpTarget, const RenderTransform* pTransform, auto pt3 = pts + 2; auto pt4 = pts + 3; - if ((FLT_SAME(pt1->x, pt2->x) && FLT_SAME(pt2->y, pt3->y) && FLT_SAME(pt3->x, pt4->x) && FLT_SAME(pt1->y, pt4->y)) || - (FLT_SAME(pt2->x, pt3->x) && FLT_SAME(pt1->y, pt2->y) && FLT_SAME(pt1->x, pt4->x) && FLT_SAME(pt3->y, pt4->y))) { + if ((mathEqual(pt1->x, pt2->x) && mathEqual(pt2->y, pt3->y) && mathEqual(pt3->x, pt4->x) && mathEqual(pt1->y, pt4->y)) || + (mathEqual(pt2->x, pt3->x) && mathEqual(pt1->y, pt2->y) && mathEqual(pt1->x, pt4->x) && mathEqual(pt3->y, pt4->y))) { auto x1 = pt1->x; auto y1 = pt1->y; @@ -117,9 +112,9 @@ Paint* Paint::Impl::duplicate() bool Paint::Impl::rotate(float degree) { if (rTransform) { - if (fabsf(degree - rTransform->degree) <= FLT_EPSILON) return true; + if (mathEqual(degree, rTransform->degree)) return true; } else { - if (fabsf(degree) <= FLT_EPSILON) return true; + if (mathZero(degree)) return true; rTransform = new RenderTransform(); } rTransform->degree = degree; @@ -132,9 +127,9 @@ bool Paint::Impl::rotate(float degree) bool Paint::Impl::scale(float factor) { if (rTransform) { - if (fabsf(factor - rTransform->scale) <= FLT_EPSILON) return true; + if (mathEqual(factor, rTransform->scale)) return true; } else { - if (fabsf(factor) <= FLT_EPSILON) return true; + if (mathZero(factor)) return true; rTransform = new RenderTransform(); } rTransform->scale = factor; @@ -147,9 +142,9 @@ bool Paint::Impl::scale(float factor) bool Paint::Impl::translate(float x, float y) { if (rTransform) { - if (fabsf(x - rTransform->x) <= FLT_EPSILON && fabsf(y - rTransform->y) <= FLT_EPSILON) return true; + if (mathEqual(x, rTransform->x) && mathEqual(y, rTransform->y)) return true; } else { - if (fabsf(x) <= FLT_EPSILON && fabsf(y) <= FLT_EPSILON) return true; + if (mathZero(x) && mathZero(y)) return true; rTransform = new RenderTransform(); } rTransform->x = x; diff --git a/src/lib/tvgRender.cpp b/src/lib/tvgRender.cpp index fbae2ca..a48075d 100644 --- a/src/lib/tvgRender.cpp +++ b/src/lib/tvgRender.cpp @@ -48,16 +48,13 @@ bool RenderTransform::update() if (overriding) return true; //Init Status - if (fabsf(x) <= FLT_EPSILON && fabsf(y) <= FLT_EPSILON && - fabsf(degree) <= FLT_EPSILON && fabsf(scale - 1) <= FLT_EPSILON) { - return false; - } + if (mathZero(x) && mathZero(y) && mathZero(degree) && mathEqual(scale, 1)) return false; mathIdentity(&m); mathScale(&m, scale); - if (fabsf(degree) > FLT_EPSILON) mathRotate(&m, degree); + if (!mathZero(degree)) mathRotate(&m, degree); mathTranslate(&m, x, y); diff --git a/src/lib/tvgShape.cpp b/src/lib/tvgShape.cpp index afaf267..42b2c0d 100644 --- a/src/lib/tvgShape.cpp +++ b/src/lib/tvgShape.cpp @@ -20,10 +20,7 @@ * SOFTWARE. */ -#define _USE_MATH_DEFINES //Math Constants are not defined in Standard C/C++. - -#include -#include +#include "tvgMath.h" #include "tvgShapeImpl.h" /************************************************************************/ @@ -171,7 +168,7 @@ Result Shape::appendArc(float cx, float cy, float radius, float startAngle, floa auto nCurves = ceil(fabsf(sweep / float(M_PI_2))); auto sweepSign = (sweep < 0 ? -1 : 1); auto fract = fmodf(sweep, float(M_PI_2)); - fract = (fabsf(fract) < FLT_EPSILON) ? float(M_PI_2) * sweepSign : fract; + fract = (mathZero(fract)) ? float(M_PI_2) * sweepSign : fract; //Start from here Point start = {radius * cosf(startAngle), radius * sinf(startAngle)}; @@ -238,7 +235,7 @@ Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry) pImpl->path.lineTo(x, y + h); pImpl->path.close(); //circle - } else if (fabsf(rx - halfW) < FLT_EPSILON && fabsf(ry - halfH) < FLT_EPSILON) { + } else if (mathEqual(rx, halfW) && mathEqual(ry, halfH)) { return appendCircle(x + (w * 0.5f), y + (h * 0.5f), rx, ry); } else { auto hrx = rx * 0.5f; diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index a7403d9..0b5c120 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -1472,8 +1472,8 @@ static bool _attrParseRectNode(void* data, const char* key, const char* value) if (!strncmp(rectTags[i].tag, "rx", sz)) rect->hasRx = true; if (!strncmp(rectTags[i].tag, "ry", sz)) rect->hasRy = true; - if ((rect->rx > FLT_EPSILON) && (rect->ry <= FLT_EPSILON) && rect->hasRx && !rect->hasRy) rect->ry = rect->rx; - if ((rect->ry > FLT_EPSILON) && (rect->rx <= FLT_EPSILON) && !rect->hasRx && rect->hasRy) rect->rx = rect->ry; + if ((rect->rx >= FLT_EPSILON) && (rect->ry < FLT_EPSILON) && rect->hasRx && !rect->hasRy) rect->ry = rect->rx; + if ((rect->ry >= FLT_EPSILON) && (rect->rx < FLT_EPSILON) && !rect->hasRx && rect->hasRy) rect->rx = rect->ry; return ret; } } diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index 7363a71..6737053 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -48,13 +48,13 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include + #include +#include "tvgMath.h" #include "tvgSvgLoaderCommon.h" #include "tvgSvgSceneBuilder.h" #include "tvgSvgPath.h" #include "tvgSvgUtil.h" -#include static bool _appendShape(SvgNode* node, Shape* shape, float vx, float vy, float vw, float vh, const string& svgPath); static unique_ptr _sceneBuildHelper(const SvgNode* node, float vx, float vy, float vw, float vh, const string& svgPath, bool mask); @@ -598,7 +598,7 @@ unique_ptr svgSceneBuild(SvgNode* node, float vx, float vy, float vw, flo auto docNode = _sceneBuildHelper(node, vx, vy, vw, vh, svgPath, false); - if (fabsf(w - vw) > FLT_EPSILON || fabsf(h - vh) > FLT_EPSILON) { + if (!mathEqual(w, vw) || !mathEqual(h, vh)) { auto sx = w / vw; auto sy = h / vh; @@ -625,7 +625,7 @@ unique_ptr svgSceneBuild(SvgNode* node, float vx, float vy, float vw, flo Matrix m = {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1}; docNode->transform(m); } - } else if (fabs(vx) > FLT_EPSILON || fabs(vy) > FLT_EPSILON) { + } else if (!mathZero(vx) || !mathZero(vy)) { docNode->translate(-vx, -vy); } diff --git a/src/savers/tvg/tvgTvgSaver.cpp b/src/savers/tvg/tvgTvgSaver.cpp index f1648fb..46ad28e 100644 --- a/src/savers/tvg/tvgTvgSaver.cpp +++ b/src/savers/tvg/tvgTvgSaver.cpp @@ -83,9 +83,9 @@ static bool _merge(Shape* from, Shape* to) auto t1 = from->transform(); auto t2 = to->transform(); - if (fabs(t1.e11 - t2.e11) > FLT_EPSILON || fabs(t1.e12 - t2.e12) > FLT_EPSILON || fabs(t1.e13 - t2.e13) > FLT_EPSILON || - fabs(t1.e21 - t2.e21) > FLT_EPSILON || fabs(t1.e22 - t2.e22) > FLT_EPSILON || fabs(t1.e23 - t2.e23) > FLT_EPSILON || - fabs(t1.e31 - t2.e31) > FLT_EPSILON || fabs(t1.e32 - t2.e32) > FLT_EPSILON || fabs(t1.e33 - t2.e33) > FLT_EPSILON) { + if (!mathEqual(t1.e11, t2.e11) || !mathEqual(t1.e12, t2.e12) || !mathEqual(t1.e13, t2.e13) || + !mathEqual(t1.e21, t2.e21) || !mathEqual(t1.e22, t2.e22) || !mathEqual(t1.e23, t2.e23) || + !mathEqual(t1.e31, t2.e31) || !mathEqual(t1.e32, t2.e32) || !mathEqual(t1.e33, t2.e33)) { return false; } @@ -499,9 +499,9 @@ TvgBinCounter TvgSaver::serializePath(const Shape* shape, const Matrix* transfor //transform? if (preTransform) { - if (fabs(transform->e11 - 1) > FLT_EPSILON || fabs(transform->e12) > FLT_EPSILON || fabs(transform->e13) > FLT_EPSILON || - fabs(transform->e21) > FLT_EPSILON || fabs(transform->e22 - 1) > FLT_EPSILON || fabs(transform->e23) > FLT_EPSILON || - fabs(transform->e31) > FLT_EPSILON || fabs(transform->e32) > FLT_EPSILON || fabs(transform->e33 - 1) > FLT_EPSILON) { + if (!mathEqual(transform->e11, 1.0f) || !mathZero(transform->e12) || !mathZero(transform->e13) || + !mathZero(transform->e21) || !mathEqual(transform->e22, 1.0f) || !mathZero(transform->e23) || + !mathZero(transform->e31) || !mathZero(transform->e32) || !mathEqual(transform->e33, 1.0f)) { auto p = const_cast(pts); for (uint32_t i = 0; i < ptsCnt; ++i) mathMultiply(p++, transform); } @@ -535,7 +535,7 @@ TvgBinCounter TvgSaver::serializeShape(const Shape* shape, const Matrix* pTransf shape->strokeColor(color, color + 1, color + 2, color + 3); auto fill = shape->strokeFill(); if (fill || color[3] > 0) { - if (fabsf(cTransform->e11 - cTransform->e22) > FLT_EPSILON || (fabsf(cTransform->e11) < FLT_EPSILON && fabsf(cTransform->e12 - cTransform->e21) > FLT_EPSILON) || shape->strokeDash(nullptr) > 0) preTransform = false; + if (!mathEqual(cTransform->e11, cTransform->e22) || (mathZero(cTransform->e11) && !mathEqual(cTransform->e12, cTransform->e21)) || shape->strokeDash(nullptr) > 0) preTransform = false; cnt += serializeStroke(shape, cTransform, preTransform); } } @@ -756,7 +756,7 @@ bool TvgSaver::save(Paint* paint, const string& path, bool compress) if (x < 0) vsize[0] += x; if (y < 0) vsize[1] += y; - if (vsize[0] <= FLT_EPSILON || vsize[1] <= FLT_EPSILON) { + if (vsize[0] < FLT_EPSILON || vsize[1] < FLT_EPSILON) { TVGLOG("TVG_SAVER", "Saving paint(%p) has zero view size.", paint); return false; } -- 2.7.4 From 9852a41181ab95d7e51715a640131b56e1385bdc Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Mon, 15 Nov 2021 14:47:11 +0900 Subject: [PATCH 10/16] sw_engine image: ++optimization apply fast track to fast up the image rasterization. only shifted image doesn't need to have the matrix-transform computation, we can avoid it by just shifting offset xy by simple caculating. @Issue: https://github.com/Samsung/thorvg/issues/206 --- src/lib/sw_engine/tvgSwCommon.h | 5 +++ src/lib/sw_engine/tvgSwImage.cpp | 27 +++++++++++- src/lib/sw_engine/tvgSwRaster.cpp | 87 ++++++++++++++++++--------------------- 3 files changed, 72 insertions(+), 47 deletions(-) diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index 365e9b4..a4c6992 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -224,6 +224,11 @@ struct SwImage SwRleData* rle = nullptr; uint32_t* data = nullptr; uint32_t w, h, stride; + int32_t x = 0; //shift x + int32_t y = 0; //shift y + float scale; + + bool transformed = false; }; struct SwBlender diff --git a/src/lib/sw_engine/tvgSwImage.cpp b/src/lib/sw_engine/tvgSwImage.cpp index 5167067..096e734 100644 --- a/src/lib/sw_engine/tvgSwImage.cpp +++ b/src/lib/sw_engine/tvgSwImage.cpp @@ -67,6 +67,13 @@ static bool _genOutline(SwImage* image, const Matrix* transform, SwMpool* mpool, } +static inline bool _onlyShifted(const Matrix* m) +{ + if (mathEqual(m->e11, 1.0f) && mathEqual(m->e22, 1.0f) && mathZero(m->e12) && mathZero(m->e21)) return true; + return false; +} + + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ @@ -74,8 +81,26 @@ static bool _genOutline(SwImage* image, const Matrix* transform, SwMpool* mpool, bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid) { + image->transformed = !_onlyShifted(transform); + bool fastTrack; + + //Figure out the scale factor by transform matrix + if (image->transformed) { + auto scaleX = sqrtf((transform->e11 * transform->e11) + (transform->e21 * transform->e21)); + auto scaleY = sqrtf((transform->e22 * transform->e22) + (transform->e12 * transform->e12)); + //TODO:If the x and y axis scale is different, a separate interpolation algorithm for each axis should be applied. + image->scale = (fabsf(scaleX - scaleY) > 0.01f) ? 1.0f : scaleX; + fastTrack = false; + //Fast track: Non-transformed image but just shifted. + } else { + image->scale = 1.0f; + image->x = -static_cast(round(transform->e13)); + image->y = -static_cast(round(transform->e23)); + fastTrack = true; + } + if (!_genOutline(image, transform, mpool, tid)) return false; - return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, false); + return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, fastTrack); } diff --git a/src/lib/sw_engine/tvgSwRaster.cpp b/src/lib/sw_engine/tvgSwRaster.cpp index e3640bd..0466c75 100644 --- a/src/lib/sw_engine/tvgSwRaster.cpp +++ b/src/lib/sw_engine/tvgSwRaster.cpp @@ -243,11 +243,11 @@ static bool _translucentImageRle(SwSurface* surface, const SwImage* image, uint3 for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { auto dst = &surface->buffer[span->y * surface->stride + span->x]; - auto src = image->data + span->x + span->y * image->stride; + auto img = image->data + (span->y + image->y) * image->stride + (span->x + image->x); auto alpha = ALPHA_MULTIPLY(span->coverage, opacity); - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++src) { - *src = ALPHA_BLEND(*src, alpha); - *dst = *src + ALPHA_BLEND(*dst, 255 - surface->blender.alpha(*src)); + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { + auto src = ALPHA_BLEND(*img, alpha); + *dst = src + ALPHA_BLEND(*dst, 255 - surface->blender.alpha(src)); } } return true; @@ -259,22 +259,21 @@ static bool _translucentImageRleMask(SwSurface* surface, const SwImage* image, u TVGLOG("SW_ENGINE", "Image Rle Alpha Mask / Inverse Alpha Mask Composition"); auto span = image->rle->spans; - auto img = image->data; auto cbuffer = surface->compositor->image.data; for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { auto dst = &surface->buffer[span->y * surface->stride + span->x]; auto cmp = &cbuffer[span->y * surface->stride + span->x]; - auto src = img + span->y * image->stride + span->x; + auto img = image->data + (span->y + image->y) * image->stride + (span->x + image->x); auto alpha = ALPHA_MULTIPLY(span->coverage, opacity); if (alpha == 255) { - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++src) { - auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp)); + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++img) { + auto tmp = ALPHA_BLEND(*img, blendMethod(*cmp)); *dst = tmp + ALPHA_BLEND(*dst, surface->blender.ialpha(tmp)); } } else { - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++src) { - auto tmp = ALPHA_BLEND(*src, ALPHA_MULTIPLY(alpha, blendMethod(*cmp))); + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++img) { + auto tmp = ALPHA_BLEND(*img, ALPHA_MULTIPLY(alpha, blendMethod(*cmp))); *dst = tmp + ALPHA_BLEND(*dst, surface->blender.ialpha(tmp)); } } @@ -554,10 +553,16 @@ static bool _rasterImageRle(SwSurface* surface, const SwImage* image) for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { auto dst = &surface->buffer[span->y * surface->stride + span->x]; - auto src = image->data + span->x + span->y * image->stride; - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++src) { - *src = ALPHA_BLEND(*src, span->coverage); - *dst = *src + ALPHA_BLEND(*dst, 255 - surface->blender.alpha(*src)); + auto img = image->data + (span->y + image->y) * image->stride + (span->x + image->x); + if (span->coverage == 255) { + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { + *dst = *img; + } + } else { + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { + auto src = ALPHA_BLEND(*img, span->coverage); + *dst = src + ALPHA_BLEND(*dst, 255 - surface->blender.alpha(src)); + } } } return true; @@ -858,7 +863,7 @@ static bool _rasterTranslucentDownScaleImage(SwSurface* surface, const SwImage* static bool _translucentImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const SwBBox& region) { auto dbuffer = &surface->buffer[region.min.y * surface->stride + region.min.x]; - auto sbuffer = image->data + region.min.x + region.min.y * image->stride; + auto sbuffer = image->data + (region.min.y + image->y) * image->stride + (region.min.x + image->x); for (auto y = region.min.y; y < region.max.y; ++y) { auto dst = dbuffer; @@ -882,7 +887,7 @@ static bool _translucentImageMask(SwSurface* surface, const SwImage* image, uint TVGLOG("SW_ENGINE", "Image Alpha Mask / Inverse Alpha Mask Composition"); - auto sbuffer = image->data + (region.min.y * image->w) + region.min.x; + auto sbuffer = image->data + (region.min.y + image->y) * image->stride + (region.min.x + image->x); auto cbuffer = surface->compositor->image.data + (region.min.y * surface->stride) + region.min.x; //compositor buffer for (uint32_t y = 0; y < h2; ++y) { @@ -918,7 +923,7 @@ static bool _rasterTranslucentImage(SwSurface* surface, const SwImage* image, ui static bool _rasterImage(SwSurface* surface, const SwImage* image, const SwBBox& region) { auto dbuffer = &surface->buffer[region.min.y * surface->stride + region.min.x]; - auto sbuffer = image->data + region.min.x + region.min.y * image->stride; + auto sbuffer = image->data + (region.min.y + image->y) * image->stride + (region.min.x + image->x); for (auto y = region.min.y; y < region.max.y; ++y) { auto dst = dbuffer; @@ -1532,57 +1537,47 @@ void rasterUnpremultiply(SwSurface* surface) bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& bbox, uint32_t opacity) { - Matrix itransform; - auto scale = 1.0f; - bool transformed = false; + static constexpr float DOWN_SCALE_TOLERANCE = 0.5f; - //Figure out the scale factor by transform matrix - if (transform) { - if (!mathInverse(transform, &itransform)) return false; - scale = sqrtf((transform->e11 * transform->e11) + (transform->e21 * transform->e21)); - auto scaleY = sqrtf((transform->e22 * transform->e22) + (transform->e12 * transform->e12)); - //TODO:If the x and y axis scale is different, a separate interpolation algorithm for each axis should be applied. - if (fabsf(scale - scaleY) > 0.01f) scale = 1.0f; - if (!mathIdentity(transform)) transformed = true; - } else mathIdentity(&itransform); + uint32_t halfScale = static_cast(0.5f / image->scale); + if (halfScale == 0) halfScale = 1; auto translucent = _translucent(surface, opacity); - auto downScaleTolerance = 0.5f; - auto halfScale = static_cast(0.5f / scale); - if (halfScale == 0) halfScale = 1; //Clipped Image if (image->rle) { - if (transformed) { + if (image->transformed) { + Matrix itransform; + if (!mathInverse(transform, &itransform)) return false; if (translucent) { - if (mathEqual(scale, 1.0f)) return _rasterTranslucentImageRle(surface, image, opacity, &itransform); - else if (scale < downScaleTolerance) return _rasterTranslucentDownScaleImageRle(surface, image, opacity, &itransform, halfScale); + if (mathEqual(image->scale, 1.0f)) return _rasterTranslucentImageRle(surface, image, opacity, &itransform); + else if (image->scale < DOWN_SCALE_TOLERANCE) return _rasterTranslucentDownScaleImageRle(surface, image, opacity, &itransform, halfScale); else return _rasterTranslucentUpScaleImageRle(surface, image, opacity, &itransform); } else { - if (mathEqual(scale, 1.0f)) return _rasterImageRle(surface, image, &itransform); - else if (scale < downScaleTolerance) return _rasterDownScaleImageRle(surface, image, &itransform, halfScale); + if (mathEqual(image->scale, 1.0f)) return _rasterImageRle(surface, image, &itransform); + else if (image->scale < DOWN_SCALE_TOLERANCE) return _rasterDownScaleImageRle(surface, image, &itransform, halfScale); else return _rasterUpScaleImageRle(surface, image, &itransform); } - //Fast track - //OPTIMIZE ME: Support non transformed image. Only shifted image can use these routines. + //Fast track: Only shifted image can go into this routine. } else { if (translucent) return _rasterTranslucentImageRle(surface, image, opacity); return _rasterImageRle(surface, image); } //Whole Image } else { - if (transformed) { + if (image->transformed) { + Matrix itransform; + if (!mathInverse(transform, &itransform)) return false; if (translucent) { - if (mathEqual(scale, 1.0f)) return _rasterTranslucentImage(surface, image, opacity, bbox, &itransform); - else if (scale < downScaleTolerance) return _rasterTranslucentDownScaleImage(surface, image, opacity, bbox, &itransform, halfScale); + if (mathEqual(image->scale, 1.0f)) return _rasterTranslucentImage(surface, image, opacity, bbox, &itransform); + else if (image->scale < DOWN_SCALE_TOLERANCE) return _rasterTranslucentDownScaleImage(surface, image, opacity, bbox, &itransform, halfScale); else return _rasterTranslucentUpScaleImage(surface, image, opacity, bbox, &itransform); } else { - if (mathEqual(scale, 1.0f)) return _rasterImage(surface, image, bbox, &itransform); - else if (scale < downScaleTolerance) return _rasterDownScaleImage(surface, image, bbox, &itransform, halfScale); + if (mathEqual(image->scale, 1.0f)) return _rasterImage(surface, image, bbox, &itransform); + else if (image->scale < DOWN_SCALE_TOLERANCE) return _rasterDownScaleImage(surface, image, bbox, &itransform, halfScale); else return _rasterUpScaleImage(surface, image, bbox, &itransform); } - //Fast track - //OPTIMIZE ME: Support non transformed image. Only shifted image can use these routines. + //Fast track: Only shifted image can go into this routine. } else { if (translucent) return _rasterTranslucentImage(surface, image, opacity, bbox); return _rasterImage(surface, image, bbox); -- 2.7.4 From bfd0bb50cbb10e0ddbca6b0eff6b98215baa488c Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Mon, 15 Nov 2021 18:06:59 +0900 Subject: [PATCH 11/16] examples pictures: updated for case cover. + test opacity --- src/examples/PictureJpg.cpp | 3 +++ src/examples/PicturePng.cpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/examples/PictureJpg.cpp b/src/examples/PictureJpg.cpp index 7f4e6c5..0dc6d35 100644 --- a/src/examples/PictureJpg.cpp +++ b/src/examples/PictureJpg.cpp @@ -31,6 +31,8 @@ void tvgDrawCmds(tvg::Canvas* canvas) { if (!canvas) return; + auto opacity = 51; + //Load jpg file from path for (int i = 0; i < 5; ++i) { auto picture = tvg::Picture::gen(); @@ -41,6 +43,7 @@ void tvgDrawCmds(tvg::Canvas* canvas) picture->translate(i* 150, i * 150); picture->rotate(30 * i); picture->size(200, 200); + picture->opacity(opacity + opacity * i); if (canvas->push(move(picture)) != tvg::Result::Success) return; } diff --git a/src/examples/PicturePng.cpp b/src/examples/PicturePng.cpp index d7ad208..1cccf69 100644 --- a/src/examples/PicturePng.cpp +++ b/src/examples/PicturePng.cpp @@ -32,6 +32,8 @@ void tvgDrawCmds(tvg::Canvas* canvas) if (!canvas) return; //Load png file from path + auto opacity = 51; + for (int i = 0; i < 5; ++i) { auto picture = tvg::Picture::gen(); if (picture->load(EXAMPLE_DIR"/test.png") != tvg::Result::Success) { @@ -41,6 +43,7 @@ void tvgDrawCmds(tvg::Canvas* canvas) picture->translate(i* 150, i * 150); picture->rotate(30 * i); picture->size(200, 200); + picture->opacity(opacity + opacity * i); if (canvas->push(move(picture)) != tvg::Result::Success) return; } -- 2.7.4 From dd8a0ddec587e74cdffa8a06e73e44638a11527d Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Tue, 16 Nov 2021 02:45:06 +0100 Subject: [PATCH 12/16] sw_engine: ialpha function pointer used instead of 255 - alpha --- src/lib/sw_engine/tvgSwRaster.cpp | 44 +++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/lib/sw_engine/tvgSwRaster.cpp b/src/lib/sw_engine/tvgSwRaster.cpp index 0466c75..2b4c755 100644 --- a/src/lib/sw_engine/tvgSwRaster.cpp +++ b/src/lib/sw_engine/tvgSwRaster.cpp @@ -247,7 +247,7 @@ static bool _translucentImageRle(SwSurface* surface, const SwImage* image, uint3 auto alpha = ALPHA_MULTIPLY(span->coverage, opacity); for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { auto src = ALPHA_BLEND(*img, alpha); - *dst = src + ALPHA_BLEND(*dst, 255 - surface->blender.alpha(src)); + *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); } } return true; @@ -313,7 +313,7 @@ static bool _translucentImageRle(SwSurface* surface, const SwImage* image, uint3 auto rY = static_cast(roundf((span->x + x) * itransform->e21 + ey2)); if (rX >= w || rY >= h) continue; auto src = ALPHA_BLEND(img[rY * image->stride + rX], alpha); - *dst = src + ALPHA_BLEND(*dst, 255 - surface->blender.alpha(src)); + *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); } } return true; @@ -394,7 +394,7 @@ static bool _translucentUpScaleImageRle(SwSurface* surface, const SwImage* image uint32_t src; if (rX == w - 1 || rY == h - 1) src = ALPHA_BLEND(img[rY * image->stride + rX], alpha); else src = ALPHA_BLEND(_applyBilinearInterpolation(img, image->stride, h, fX, fY), alpha); - *dst = src + ALPHA_BLEND(*dst, 255 - surface->blender.alpha(src)); + *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); } } return true; @@ -482,7 +482,7 @@ static bool _translucentDownScaleImageRle(SwSurface* surface, const SwImage* ima uint32_t src; if (rX < halfScale || rY < halfScale || rX >= w - halfScale || rY >= h - halfScale) src = ALPHA_BLEND(img[rY * image->stride + rX], alpha); else src = ALPHA_BLEND(_average2Nx2NPixel(surface, img, image->stride, h, rX, rY, halfScale), alpha); - *dst = src + ALPHA_BLEND(*dst, 255 - surface->blender.alpha(src)); + *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); } } return true; @@ -561,7 +561,7 @@ static bool _rasterImageRle(SwSurface* surface, const SwImage* image) } else { for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { auto src = ALPHA_BLEND(*img, span->coverage); - *dst = src + ALPHA_BLEND(*dst, 255 - surface->blender.alpha(src)); + *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); } } } @@ -585,7 +585,7 @@ static bool _rasterImageRle(SwSurface* surface, const SwImage* image, const Matr auto rY = static_cast(roundf((span->x + x) * itransform->e21 + ey2)); if (rX >= w || rY >= h) continue; auto src = ALPHA_BLEND(img[rY * image->stride + rX], span->coverage); - *dst = src + ALPHA_BLEND(*dst, 255 - surface->blender.alpha(src)); + *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); } } return true; @@ -612,7 +612,7 @@ static bool _rasterUpScaleImageRle(SwSurface* surface, const SwImage* image, con uint32_t src; if (rX == w - 1 || rY == h - 1) src = ALPHA_BLEND(img[rY * image->stride + rX], span->coverage); else src = ALPHA_BLEND(_applyBilinearInterpolation(img, image->stride, h, fX, fY), span->coverage); - *dst = src + ALPHA_BLEND(*dst, 255 - surface->blender.alpha(src)); + *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); } } return true; @@ -637,7 +637,7 @@ static bool _rasterDownScaleImageRle(SwSurface* surface, const SwImage* image, c uint32_t src; if (rX < halfScale || rY < halfScale || rX >= w - halfScale || rY >= h - halfScale) src = ALPHA_BLEND(img[rY * image->stride + rX], span->coverage); else src = ALPHA_BLEND(_average2Nx2NPixel(surface, img, image->stride, h, rX, rY, halfScale), span->coverage); - *dst = src + ALPHA_BLEND(*dst, 255 - surface->blender.alpha(src)); + *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); } } return true; @@ -660,7 +660,7 @@ static bool _translucentImage(SwSurface* surface, const SwImage* image, uint32_t auto rY = static_cast(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, 255 - surface->blender.alpha(src)); + *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); } dbuffer += surface->stride; } @@ -731,7 +731,7 @@ static bool _translucentUpScaleImage(SwSurface* surface, const SwImage* image, u uint32_t src; if (rX == w - 1 || rY == h - 1) src = ALPHA_BLEND(img[rX + (rY * image->stride)], opacity); else src = ALPHA_BLEND(_applyBilinearInterpolation(img, image->stride, h, fX, fY), opacity); - *dst = src + ALPHA_BLEND(*dst, 255 - surface->blender.alpha(src)); + *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); } dbuffer += surface->stride; } @@ -804,7 +804,7 @@ static bool _translucentDownScaleImage(SwSurface* surface, const SwImage* image, 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(_average2Nx2NPixel(surface, img, w, h, rX, rY, halfScale), opacity); - *dst = src + ALPHA_BLEND(*dst, 255 - surface->blender.alpha(src)); + *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); } dbuffer += surface->stride; } @@ -870,7 +870,7 @@ static bool _translucentImage(SwSurface* surface, const SwImage* image, uint32_t auto src = sbuffer; for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) { auto p = ALPHA_BLEND(*src, opacity); - *dst = p + ALPHA_BLEND(*dst, 255 - surface->blender.alpha(p)); + *dst = p + ALPHA_BLEND(*dst, surface->blender.ialpha(p)); } dbuffer += surface->stride; sbuffer += image->stride; @@ -929,7 +929,7 @@ static bool _rasterImage(SwSurface* surface, const SwImage* image, const SwBBox& auto dst = dbuffer; auto src = sbuffer; for (auto x = region.min.x; x < region.max.x; x++, dst++, src++) { - *dst = *src + ALPHA_BLEND(*dst, 255 - surface->blender.alpha(*src)); + *dst = *src + ALPHA_BLEND(*dst, surface->blender.ialpha(*src)); } dbuffer += surface->stride; sbuffer += image->stride; @@ -953,7 +953,7 @@ static bool _rasterImage(SwSurface* surface, const SwImage* image, const SwBBox& auto rY = static_cast(roundf(x * itransform->e21 + ey2)); if (rX >= w || rY >= h) continue; auto src = img[rX + (rY * image->stride)]; - *dst = src + ALPHA_BLEND(*dst, 255 - surface->blender.alpha(src)); + *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); } } return true; @@ -979,7 +979,7 @@ static bool _rasterUpScaleImage(SwSurface* surface, const SwImage* image, const uint32_t src; if (rX == w - 1 || rY == h - 1) src = img[rX + (rY * w)]; else src = _applyBilinearInterpolation(img, w, h, fX, fY); - *dst = src + ALPHA_BLEND(*dst, 255 - surface->blender.alpha(src)); + *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); } } return true; @@ -1006,7 +1006,7 @@ static bool _rasterDownScaleImage(SwSurface* surface, const SwImage* image, cons uint32_t src; if (rX < halfScale || rY < halfScale || rX >= w - halfScale || rY >= h - halfScale) src = img[rX + (rY * w)]; else src = _average2Nx2NPixel(surface, img, w, h, rX, rY, halfScale); - *dst = src + ALPHA_BLEND(*dst, 255 - surface->blender.alpha(src)); + *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); } } return true; @@ -1032,7 +1032,7 @@ static bool _translucentLinearGradientRect(SwSurface* surface, const SwBBox& reg for (uint32_t y = 0; y < h; ++y) { fillFetchLinear(fill, sbuffer, region.min.y + y, region.min.x, w); for (uint32_t x = 0; x < w; ++x) { - dst[x] = sbuffer[x] + ALPHA_BLEND(dst[x], 255 - surface->blender.alpha(sbuffer[x])); + dst[x] = sbuffer[x] + ALPHA_BLEND(dst[x], surface->blender.ialpha(sbuffer[x])); } dst += surface->stride; } @@ -1112,7 +1112,7 @@ static bool _translucentRadialGradientRect(SwSurface* surface, const SwBBox& reg for (uint32_t y = 0; y < h; ++y) { fillFetchRadial(fill, sbuffer, region.min.y + y, region.min.x, w); for (uint32_t x = 0; x < w; ++x) { - dst[x] = sbuffer[x] + ALPHA_BLEND(dst[x], 255 - surface->blender.alpha(sbuffer[x])); + dst[x] = sbuffer[x] + ALPHA_BLEND(dst[x], surface->blender.ialpha(sbuffer[x])); } dst += surface->stride; } @@ -1191,12 +1191,12 @@ static bool _translucentLinearGradientRle(SwSurface* surface, const SwRleData* r fillFetchLinear(fill, buffer, span->y, span->x, span->len); if (span->coverage == 255) { for (uint32_t i = 0; i < span->len; ++i) { - dst[i] = buffer[i] + ALPHA_BLEND(dst[i], 255 - surface->blender.alpha(buffer[i])); + dst[i] = buffer[i] + ALPHA_BLEND(dst[i], surface->blender.ialpha(buffer[i])); } } else { for (uint32_t i = 0; i < span->len; ++i) { auto tmp = ALPHA_BLEND(buffer[i], span->coverage); - dst[i] = tmp + ALPHA_BLEND(dst[i], 255 - surface->blender.alpha(tmp)); + dst[i] = tmp + ALPHA_BLEND(dst[i], surface->blender.ialpha(tmp)); } } } @@ -1290,12 +1290,12 @@ static bool _translucentRadialGradientRle(SwSurface* surface, const SwRleData* r fillFetchRadial(fill, buffer, span->y, span->x, span->len); if (span->coverage == 255) { for (uint32_t i = 0; i < span->len; ++i) { - dst[i] = buffer[i] + ALPHA_BLEND(dst[i], 255 - surface->blender.alpha(buffer[i])); + dst[i] = buffer[i] + ALPHA_BLEND(dst[i], surface->blender.ialpha(buffer[i])); } } else { for (uint32_t i = 0; i < span->len; ++i) { auto tmp = ALPHA_BLEND(buffer[i], span->coverage); - dst[i] = tmp + ALPHA_BLEND(dst[i], 255 - surface->blender.alpha(tmp)); + dst[i] = tmp + ALPHA_BLEND(dst[i], surface->blender.ialpha(tmp)); } } } -- 2.7.4 From 677982133875cd9f2e4cae78d8fce088d576a8ea Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Tue, 16 Nov 2021 17:18:51 +0900 Subject: [PATCH 13/16] sw_engine neon: code refactoring. keep the internal function name consistency. --- src/lib/sw_engine/tvgSwRasterNeon.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/sw_engine/tvgSwRasterNeon.h b/src/lib/sw_engine/tvgSwRasterNeon.h index fd391b9..efc69e9 100644 --- a/src/lib/sw_engine/tvgSwRasterNeon.h +++ b/src/lib/sw_engine/tvgSwRasterNeon.h @@ -24,7 +24,7 @@ #include -static inline uint8x8_t ALPHA_BLEND_NEON(uint8x8_t c, uint8x8_t a) +static inline uint8x8_t ALPHA_BLEND(uint8x8_t c, uint8x8_t a) { uint16x8_t t = vmull_u8(c, a); return vshrn_n_u16(t, 8); @@ -77,7 +77,7 @@ static inline bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* uint8x8_t vIalpha = vdup_n_u8((uint8_t) ialpha); for (uint32_t x = 0; x < (span->len - align) / 2; ++x) - vDst[x] = vadd_u8(vSrc, ALPHA_BLEND_NEON(vDst[x], vIalpha)); + vDst[x] = vadd_u8(vSrc, ALPHA_BLEND(vDst[x], vIalpha)); auto leftovers = (span->len - align) % 2; if (leftovers > 0) dst[span->len - 1] = src + ALPHA_BLEND(dst[span->len - 1], ialpha); @@ -115,7 +115,7 @@ static inline bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& r } for (uint32_t x = 0; x < (w - align) / 2; ++x) - vDst[x] = vadd_u8((uint8x8_t)vColor, ALPHA_BLEND_NEON(vDst[x], vIalpha)); + vDst[x] = vadd_u8((uint8x8_t)vColor, ALPHA_BLEND(vDst[x], vIalpha)); auto leftovers = (w - align) % 2; if (leftovers > 0) dst[w - 1] = color + ALPHA_BLEND(dst[w - 1], ialpha); -- 2.7.4 From bc7f82a506d4a288f27609f7d8a3ad8da6442f08 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Tue, 16 Nov 2021 19:25:18 +0900 Subject: [PATCH 14/16] sw_engine raster: code refactoring clarfy & regroup the internal functions which have been messed up by rendering options. no logical changes --- src/lib/sw_engine/tvgSwCommon.h | 5 - src/lib/sw_engine/tvgSwRaster.cpp | 1047 +++++++++++++++++++------------------ 2 files changed, 536 insertions(+), 516 deletions(-) diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index a4c6992..e0db06b 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -280,11 +280,6 @@ static inline uint32_t COLOR_INTERPOLATE(uint32_t c1, uint32_t a1, uint32_t c2, return (c1 |= t); } -static inline uint32_t ALPHA_MULTIPLY(uint32_t c, uint32_t a) -{ - return ((c * a + 0xff) >> 8); -} - static inline SwCoord HALF_STROKE(float width) { return TO_SWCOORD(width * 0.5f); diff --git a/src/lib/sw_engine/tvgSwRaster.cpp b/src/lib/sw_engine/tvgSwRaster.cpp index 2b4c755..39d01b4 100644 --- a/src/lib/sw_engine/tvgSwRaster.cpp +++ b/src/lib/sw_engine/tvgSwRaster.cpp @@ -29,6 +29,14 @@ /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ +constexpr auto DOWN_SCALE_TOLERANCE = 0.5f; + + +static inline uint32_t _multiplyAlpha(uint32_t c, uint32_t a) +{ + return ((c * a + 0xff) >> 8); +} + static uint32_t _colorAlpha(uint32_t c) { @@ -79,6 +87,7 @@ static uint32_t _applyBilinearInterpolation(const uint32_t *img, uint32_t w, uin return COLOR_INTERPOLATE(COLOR_INTERPOLATE(c1, 255 - dX, c2, dX), 255 - dY, COLOR_INTERPOLATE(c4, 255 - dX, c3, dX), dY); } + static uint32_t _average2Nx2NPixel(SwSurface* surface, const uint32_t *img, uint32_t w, uint32_t h, uint32_t rX, uint32_t rY, uint32_t n) { uint32_t c[4] = { 0 }; @@ -104,7 +113,7 @@ static uint32_t _average2Nx2NPixel(SwSurface* surface, const uint32_t *img, uint /* Rect */ /************************************************************************/ -static bool _translucentRectMask(SwSurface* surface, const SwBBox& region, uint32_t color, uint32_t (*blendMethod)(uint32_t)) +static bool _rasterTranslucentMaskedRect(SwSurface* surface, const SwBBox& region, uint32_t color, uint32_t (*blendMethod)(uint32_t)) { auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x; auto h = static_cast(region.max.y - region.min.y); @@ -130,10 +139,10 @@ static bool _rasterTranslucentRect(SwSurface* surface, const SwBBox& region, uin { if (surface->compositor) { if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _translucentRectMask(surface, region, color, surface->blender.alpha); + return _rasterTranslucentMaskedRect(surface, region, color, surface->blender.alpha); } if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _translucentRectMask(surface, region, color, surface->blender.ialpha); + return _rasterTranslucentMaskedRect(surface, region, color, surface->blender.ialpha); } } @@ -164,7 +173,7 @@ static bool _rasterSolidRect(SwSurface* surface, const SwBBox& region, uint32_t /* Rle */ /************************************************************************/ -static bool _translucentRleMask(SwSurface* surface, SwRleData* rle, uint32_t color, uint32_t (*blendMethod)(uint32_t)) +static bool _rasterTranslucentMaskedRle(SwSurface* surface, SwRleData* rle, uint32_t color, uint32_t (*blendMethod)(uint32_t)) { TVGLOG("SW_ENGINE", "Rle Alpha Mask / Inverse Alpha Mask Composition"); @@ -193,10 +202,10 @@ static bool _rasterTranslucentRle(SwSurface* surface, SwRleData* rle, uint32_t c if (surface->compositor) { if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _translucentRleMask(surface, rle, color, surface->blender.alpha); + return _rasterTranslucentMaskedRle(surface, rle, color, surface->blender.alpha); } if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _translucentRleMask(surface, rle, color, surface->blender.ialpha); + return _rasterTranslucentMaskedRle(surface, rle, color, surface->blender.ialpha); } } @@ -234,46 +243,40 @@ static bool _rasterSolidRle(SwSurface* surface, const SwRleData* rle, uint32_t c /************************************************************************/ -/* Image */ +/* RLE Transformed Translucent Image */ /************************************************************************/ -static bool _translucentImageRle(SwSurface* surface, const SwImage* image, uint32_t opacity) -{ - auto span = image->rle->spans; - - for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto dst = &surface->buffer[span->y * surface->stride + span->x]; - auto img = image->data + (span->y + image->y) * image->stride + (span->x + image->x); - auto alpha = ALPHA_MULTIPLY(span->coverage, opacity); - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { - auto src = ALPHA_BLEND(*img, alpha); - *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); - } - } - return true; -} - - -static bool _translucentImageRleMask(SwSurface* surface, const SwImage* image, uint32_t opacity, uint32_t (*blendMethod)(uint32_t)) +static bool _rasterTransformedMaskedRleImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const Matrix* itransform, uint32_t (*blendMethod)(uint32_t)) { - TVGLOG("SW_ENGINE", "Image Rle Alpha Mask / Inverse Alpha Mask Composition"); + TVGLOG("SW_ENGINE", "Transformed Image Rle Alpha Mask / Inverse Alpha Mask Composition"); auto span = image->rle->spans; + auto img = image->data; + auto w = image->w; + auto h = image->h; auto cbuffer = surface->compositor->image.data; for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { + auto ey1 = span->y * itransform->e12 + itransform->e13; + auto ey2 = span->y * itransform->e22 + itransform->e23; auto dst = &surface->buffer[span->y * surface->stride + span->x]; auto cmp = &cbuffer[span->y * surface->stride + span->x]; - auto img = image->data + (span->y + image->y) * image->stride + (span->x + image->x); - auto alpha = ALPHA_MULTIPLY(span->coverage, opacity); + auto alpha = _multiplyAlpha(span->coverage, opacity); if (alpha == 255) { - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++img) { - auto tmp = ALPHA_BLEND(*img, blendMethod(*cmp)); + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp) { + auto rX = static_cast(roundf((span->x + x) * itransform->e11 + ey1)); + auto rY = static_cast(roundf((span->x + x) * itransform->e21 + ey2)); + if (rX >= w || rY >= h) continue; + auto tmp = ALPHA_BLEND(img[rY * image->stride + rX], blendMethod(*cmp)); *dst = tmp + ALPHA_BLEND(*dst, surface->blender.ialpha(tmp)); } } else { - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++img) { - auto tmp = ALPHA_BLEND(*img, ALPHA_MULTIPLY(alpha, blendMethod(*cmp))); + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp) { + auto rX = static_cast(roundf((span->x + x) * itransform->e11 + ey1)); + auto rY = static_cast(roundf((span->x + x) * itransform->e21 + ey2)); + if (rX >= w || rY >= h) continue; + auto src = ALPHA_BLEND(img[rY * image->stride + rX], alpha); + auto tmp = ALPHA_BLEND(src, blendMethod(*cmp)); *dst = tmp + ALPHA_BLEND(*dst, surface->blender.ialpha(tmp)); } } @@ -282,21 +285,7 @@ static bool _translucentImageRleMask(SwSurface* surface, const SwImage* image, u } -static bool _rasterTranslucentImageRle(SwSurface* surface, const SwImage* image, uint32_t opacity) -{ - if (surface->compositor) { - if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _translucentImageRleMask(surface, image, opacity, surface->blender.alpha); - } - if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _translucentImageRleMask(surface, image, opacity, surface->blender.ialpha); - } - } - return _translucentImageRle(surface, image, opacity); -} - - -static bool _translucentImageRle(SwSurface* surface, const SwImage* image, uint32_t opacity, const Matrix* itransform) +static bool _rasterTransformedTranslucentRleImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const Matrix* itransform) { auto span = image->rle->spans; auto img = image->data; @@ -307,7 +296,7 @@ static bool _translucentImageRle(SwSurface* surface, const SwImage* image, uint3 auto ey1 = span->y * itransform->e12 + itransform->e13; auto ey2 = span->y * itransform->e22 + itransform->e23; auto dst = &surface->buffer[span->y * surface->stride + span->x]; - auto alpha = ALPHA_MULTIPLY(span->coverage, opacity); + auto alpha = _multiplyAlpha(span->coverage, opacity); for (uint32_t x = 0; x < span->len; ++x, ++dst) { auto rX = static_cast(roundf((span->x + x) * itransform->e11 + ey1)); auto rY = static_cast(roundf((span->x + x) * itransform->e21 + ey2)); @@ -320,9 +309,9 @@ static bool _translucentImageRle(SwSurface* surface, const SwImage* image, uint3 } -static bool _translucentImageRleMask(SwSurface* surface, const SwImage* image, uint32_t opacity, const Matrix* itransform, uint32_t (*blendMethod)(uint32_t)) +static bool _rasterDownScaledMaskedRleImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const Matrix* itransform, uint32_t halfScale, uint32_t (*blendMethod)(uint32_t)) { - TVGLOG("SW_ENGINE", "Transformed Image Rle Alpha Mask / Inverse Alpha Mask Composition"); + TVGLOG("SW_ENGINE", "Image Rle Alpha Mask / Inverse Alpha Mask Composition"); auto span = image->rle->spans; auto img = image->data; @@ -335,13 +324,17 @@ static bool _translucentImageRleMask(SwSurface* surface, const SwImage* image, u auto ey2 = span->y * itransform->e22 + itransform->e23; auto dst = &surface->buffer[span->y * surface->stride + span->x]; auto cmp = &cbuffer[span->y * surface->stride + span->x]; - auto alpha = ALPHA_MULTIPLY(span->coverage, opacity); + auto alpha = _multiplyAlpha(span->coverage, opacity); + if (alpha == 255) { for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp) { auto rX = static_cast(roundf((span->x + x) * itransform->e11 + ey1)); auto rY = static_cast(roundf((span->x + x) * itransform->e21 + ey2)); if (rX >= w || rY >= h) continue; - auto tmp = ALPHA_BLEND(img[rY * image->stride + rX], blendMethod(*cmp)); + uint32_t src; + if (rX < halfScale || rY < halfScale || rX >= w - halfScale || rY >= h - halfScale) src = ALPHA_BLEND(img[rY * image->stride + rX], alpha); + else src = ALPHA_BLEND(_average2Nx2NPixel(surface, img, image->stride, h, rX, rY, halfScale), alpha); + auto tmp = ALPHA_BLEND(src, blendMethod(*cmp)); *dst = tmp + ALPHA_BLEND(*dst, surface->blender.ialpha(tmp)); } } else { @@ -349,8 +342,10 @@ static bool _translucentImageRleMask(SwSurface* surface, const SwImage* image, u auto rX = static_cast(roundf((span->x + x) * itransform->e11 + ey1)); auto rY = static_cast(roundf((span->x + x) * itransform->e21 + ey2)); if (rX >= w || rY >= h) continue; - auto src = ALPHA_BLEND(img[rY * image->stride + rX], alpha); - auto tmp = ALPHA_BLEND(src, blendMethod(*cmp)); + uint32_t src; + if (rX < halfScale || rY < halfScale || rX >= w - halfScale || rY >= h - halfScale) src = ALPHA_BLEND(img[rY * image->stride + rX], alpha); + else src = ALPHA_BLEND(_average2Nx2NPixel(surface, img, image->stride, h, rX, rY, halfScale), alpha); + auto tmp = ALPHA_BLEND(src, _multiplyAlpha(alpha, blendMethod(*cmp))); *dst = tmp + ALPHA_BLEND(*dst, surface->blender.ialpha(tmp)); } } @@ -359,21 +354,7 @@ static bool _translucentImageRleMask(SwSurface* surface, const SwImage* image, u } -static bool _rasterTranslucentImageRle(SwSurface* surface, const SwImage* image, uint32_t opacity, const Matrix* itransform) -{ - if (surface->compositor) { - if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _translucentImageRleMask(surface, image, opacity, itransform, surface->blender.alpha); - } - if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _translucentImageRleMask(surface, image, opacity, itransform, surface->blender.ialpha); - } - } - return _translucentImageRle(surface, image, opacity, itransform); -} - - -static bool _translucentUpScaleImageRle(SwSurface* surface, const SwImage* image, uint32_t opacity, const Matrix* itransform) +static bool _rasterDownScaledTranslucentRleImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const Matrix* itransform, uint32_t halfScale) { auto span = image->rle->spans; auto img = image->data; @@ -384,16 +365,14 @@ static bool _translucentUpScaleImageRle(SwSurface* surface, const SwImage* image auto ey1 = span->y * itransform->e12 + itransform->e13; auto ey2 = span->y * itransform->e22 + itransform->e23; auto dst = &surface->buffer[span->y * surface->stride + span->x]; - auto alpha = ALPHA_MULTIPLY(span->coverage, opacity); + auto alpha = _multiplyAlpha(span->coverage, opacity); for (uint32_t x = 0; x < span->len; ++x, ++dst) { - auto fX = (span->x + x) * itransform->e11 + ey1; - auto fY = (span->x + x) * itransform->e21 + ey2; - auto rX = static_cast(roundf(fX)); - auto rY = static_cast(roundf(fY)); + auto rX = static_cast(roundf((span->x + x) * itransform->e11 + ey1)); + auto rY = static_cast(roundf((span->x + x) * itransform->e21 + ey2)); if (rX >= w || rY >= h) continue; uint32_t src; - if (rX == w - 1 || rY == h - 1) src = ALPHA_BLEND(img[rY * image->stride + rX], alpha); - else src = ALPHA_BLEND(_applyBilinearInterpolation(img, image->stride, h, fX, fY), alpha); + if (rX < halfScale || rY < halfScale || rX >= w - halfScale || rY >= h - halfScale) src = ALPHA_BLEND(img[rY * image->stride + rX], alpha); + else src = ALPHA_BLEND(_average2Nx2NPixel(surface, img, image->stride, h, rX, rY, halfScale), alpha); *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); } } @@ -401,7 +380,7 @@ static bool _translucentUpScaleImageRle(SwSurface* surface, const SwImage* image } -static bool _translucentUpScaleImageRleMask(SwSurface* surface, const SwImage* image, uint32_t opacity, const Matrix* itransform, uint32_t (*blendMethod)(uint32_t)) +static bool _rasterUpScaledMaskedRleImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const Matrix* itransform, uint32_t (*blendMethod)(uint32_t)) { TVGLOG("SW_ENGINE", "Image Rle Alpha Mask / Inverse Alpha Mask Composition"); @@ -416,7 +395,7 @@ static bool _translucentUpScaleImageRleMask(SwSurface* surface, const SwImage* i auto ey2 = span->y * itransform->e22 + itransform->e23; auto dst = &surface->buffer[span->y * surface->stride + span->x]; auto cmp = &cbuffer[span->y * surface->stride + span->x]; - auto alpha = ALPHA_MULTIPLY(span->coverage, opacity); + auto alpha = _multiplyAlpha(span->coverage, opacity); if (alpha == 255) { for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp) { auto fX = (span->x + x) * itransform->e11 + ey1; @@ -440,7 +419,7 @@ static bool _translucentUpScaleImageRleMask(SwSurface* surface, const SwImage* i uint32_t src; if (rX == w - 1 || rY == h - 1) src = ALPHA_BLEND(img[rY * image->stride + rX], alpha); else src = ALPHA_BLEND(_applyBilinearInterpolation(img, image->stride, h, fX, fY), alpha); - auto tmp = ALPHA_BLEND(src, ALPHA_MULTIPLY(alpha, blendMethod(*cmp))); + auto tmp = ALPHA_BLEND(src, _multiplyAlpha(alpha, blendMethod(*cmp))); *dst = tmp + ALPHA_BLEND(*dst, surface->blender.ialpha(tmp)); } } @@ -449,21 +428,7 @@ static bool _translucentUpScaleImageRleMask(SwSurface* surface, const SwImage* i } -static bool _rasterTranslucentUpScaleImageRle(SwSurface* surface, const SwImage* image, uint32_t opacity, const Matrix* itransform) -{ - if (surface->compositor) { - if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _translucentUpScaleImageRleMask(surface, image, opacity, itransform, surface->blender.alpha); - } - if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _translucentUpScaleImageRleMask(surface, image, opacity, itransform, surface->blender.ialpha); - } - } - return _translucentUpScaleImageRle(surface, image, opacity, itransform); -} - - -static bool _translucentDownScaleImageRle(SwSurface* surface, const SwImage* image, uint32_t opacity, const Matrix* itransform, uint32_t halfScale) +static bool _rasterUpScaledTranslucentRleImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const Matrix* itransform) { auto span = image->rle->spans; auto img = image->data; @@ -474,102 +439,88 @@ static bool _translucentDownScaleImageRle(SwSurface* surface, const SwImage* ima auto ey1 = span->y * itransform->e12 + itransform->e13; auto ey2 = span->y * itransform->e22 + itransform->e23; auto dst = &surface->buffer[span->y * surface->stride + span->x]; - auto alpha = ALPHA_MULTIPLY(span->coverage, opacity); + auto alpha = _multiplyAlpha(span->coverage, opacity); for (uint32_t x = 0; x < span->len; ++x, ++dst) { - auto rX = static_cast(roundf((span->x + x) * itransform->e11 + ey1)); - auto rY = static_cast(roundf((span->x + x) * itransform->e21 + ey2)); + auto fX = (span->x + x) * itransform->e11 + ey1; + auto fY = (span->x + x) * itransform->e21 + ey2; + auto rX = static_cast(roundf(fX)); + auto rY = static_cast(roundf(fY)); if (rX >= w || rY >= h) continue; uint32_t src; - if (rX < halfScale || rY < halfScale || rX >= w - halfScale || rY >= h - halfScale) src = ALPHA_BLEND(img[rY * image->stride + rX], alpha); - else src = ALPHA_BLEND(_average2Nx2NPixel(surface, img, image->stride, h, rX, rY, halfScale), alpha); + if (rX == w - 1 || rY == h - 1) src = ALPHA_BLEND(img[rY * image->stride + rX], alpha); + else src = ALPHA_BLEND(_applyBilinearInterpolation(img, image->stride, h, fX, fY), alpha); *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); } } return true; } -static bool _translucentDownScaleImageRleMask(SwSurface* surface, const SwImage* image, uint32_t opacity, const Matrix* itransform, uint32_t halfScale, uint32_t (*blendMethod)(uint32_t)) -{ - TVGLOG("SW_ENGINE", "Image Rle Alpha Mask / Inverse Alpha Mask Composition"); - - auto span = image->rle->spans; - auto img = image->data; - auto w = image->w; - auto h = image->h; - auto cbuffer = surface->compositor->image.data; - - for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto ey1 = span->y * itransform->e12 + itransform->e13; - auto ey2 = span->y * itransform->e22 + itransform->e23; - auto dst = &surface->buffer[span->y * surface->stride + span->x]; - auto cmp = &cbuffer[span->y * surface->stride + span->x]; - auto alpha = ALPHA_MULTIPLY(span->coverage, opacity); - if (alpha == 255) { - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp) { - auto rX = static_cast(roundf((span->x + x) * itransform->e11 + ey1)); - auto rY = static_cast(roundf((span->x + 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[rY * image->stride + rX], alpha); - else src = ALPHA_BLEND(_average2Nx2NPixel(surface, img, image->stride, h, rX, rY, halfScale), alpha); - auto tmp = ALPHA_BLEND(src, blendMethod(*cmp)); - *dst = tmp + ALPHA_BLEND(*dst, surface->blender.ialpha(tmp)); - } - } else { - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp) { - auto rX = static_cast(roundf((span->x + x) * itransform->e11 + ey1)); - auto rY = static_cast(roundf((span->x + 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[rY * image->stride + rX], alpha); - else src = ALPHA_BLEND(_average2Nx2NPixel(surface, img, image->stride, h, rX, rY, halfScale), alpha); - auto tmp = ALPHA_BLEND(src, ALPHA_MULTIPLY(alpha, blendMethod(*cmp))); - *dst = tmp + ALPHA_BLEND(*dst, surface->blender.ialpha(tmp)); +static bool _rasterTransformedTranslucentRleImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const Matrix* itransform, uint32_t halfScale) +{ + //Transformed + if (mathEqual(image->scale, 1.0f)) { + if (surface->compositor) { + if (surface->compositor->method == CompositeMethod::AlphaMask) { + return _rasterTransformedMaskedRleImage(surface, image, opacity, itransform, surface->blender.alpha); + } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { + return _rasterTransformedMaskedRleImage(surface, image, opacity, itransform, surface->blender.ialpha); } } - } - return true; -} - - -static bool _rasterTranslucentDownScaleImageRle(SwSurface* surface, const SwImage* image, uint32_t opacity, const Matrix* itransform, uint32_t halfScale) -{ - if (surface->compositor) { - if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _translucentDownScaleImageRleMask(surface, image, opacity, itransform, halfScale, surface->blender.alpha); + return _rasterTransformedTranslucentRleImage(surface, image, opacity, itransform); + //Transformed + Down Scaled + } else if (image->scale < DOWN_SCALE_TOLERANCE) { + if (surface->compositor) { + if (surface->compositor->method == CompositeMethod::AlphaMask) { + return _rasterDownScaledMaskedRleImage(surface, image, opacity, itransform, halfScale, surface->blender.alpha); + } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { + return _rasterDownScaledMaskedRleImage(surface, image, opacity, itransform, halfScale, surface->blender.ialpha); + } } - if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _translucentDownScaleImageRleMask(surface, image, opacity, itransform, halfScale, surface->blender.ialpha); + return _rasterDownScaledTranslucentRleImage(surface, image, opacity, itransform, halfScale); + //Transformed + Up Scaled + } else { + if (surface->compositor) { + if (surface->compositor->method == CompositeMethod::AlphaMask) { + return _rasterUpScaledMaskedRleImage(surface, image, opacity, itransform, surface->blender.alpha); + } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { + return _rasterUpScaledMaskedRleImage(surface, image, opacity, itransform, surface->blender.ialpha); + } } + return _rasterUpScaledTranslucentRleImage(surface, image, opacity, itransform); } - return _translucentDownScaleImageRle(surface, image, opacity, itransform, halfScale); } -static bool _rasterImageRle(SwSurface* surface, const SwImage* image) +/************************************************************************/ +/* RLE Transformed Solid Image */ +/************************************************************************/ + +static bool _rasterSolidRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform) { auto span = image->rle->spans; + auto img = image->data; + auto w = image->w; + auto h = image->h; for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { + auto ey1 = span->y * itransform->e12 + itransform->e13; + auto ey2 = span->y * itransform->e22 + itransform->e23; auto dst = &surface->buffer[span->y * surface->stride + span->x]; - auto img = image->data + (span->y + image->y) * image->stride + (span->x + image->x); - if (span->coverage == 255) { - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { - *dst = *img; - } - } else { - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { - auto src = ALPHA_BLEND(*img, span->coverage); - *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); - } + + for (uint32_t x = 0; x < span->len; ++x, ++dst) { + auto rX = static_cast(roundf((span->x + x) * itransform->e11 + ey1)); + auto rY = static_cast(roundf((span->x + x) * itransform->e21 + ey2)); + if (rX >= w || rY >= h) continue; + auto src = ALPHA_BLEND(img[rY * image->stride + rX], span->coverage); + *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); } } return true; } -static bool _rasterImageRle(SwSurface* surface, const SwImage* image, const Matrix* itransform) +static bool _rasterDownScaledSolidRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, uint32_t halfScale) { auto span = image->rle->spans; auto img = image->data; @@ -584,7 +535,10 @@ static bool _rasterImageRle(SwSurface* surface, const SwImage* image, const Matr auto rX = static_cast(roundf((span->x + x) * itransform->e11 + ey1)); auto rY = static_cast(roundf((span->x + x) * itransform->e21 + ey2)); if (rX >= w || rY >= h) continue; - auto src = ALPHA_BLEND(img[rY * image->stride + rX], span->coverage); + + uint32_t src; + if (rX < halfScale || rY < halfScale || rX >= w - halfScale || rY >= h - halfScale) src = ALPHA_BLEND(img[rY * image->stride + rX], span->coverage); + else src = ALPHA_BLEND(_average2Nx2NPixel(surface, img, image->stride, h, rX, rY, halfScale), span->coverage); *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); } } @@ -592,7 +546,7 @@ static bool _rasterImageRle(SwSurface* surface, const SwImage* image, const Matr } -static bool _rasterUpScaleImageRle(SwSurface* surface, const SwImage* image, const Matrix* itransform) +static bool _rasterUpScaledSolidRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform) { auto span = image->rle->spans; auto img = image->data; @@ -619,99 +573,132 @@ static bool _rasterUpScaleImageRle(SwSurface* surface, const SwImage* image, con } -static bool _rasterDownScaleImageRle(SwSurface* surface, const SwImage* image, const Matrix* itransform, uint32_t halfScale) +static bool _rasterTransformedSolidRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, uint32_t halfScale) +{ + if (mathEqual(image->scale, 1.0f)) return _rasterSolidRleImage(surface, image, itransform); + else if (image->scale < DOWN_SCALE_TOLERANCE) return _rasterDownScaledSolidRleImage(surface, image, itransform, halfScale); + else return _rasterUpScaledSolidRleImage(surface, image, itransform); +} + + +/************************************************************************/ +/* RLE Direct (Solid + Translucent) Image */ +/************************************************************************/ + +static bool _rasterDirectMaskedRleImage(SwSurface* surface, const SwImage* image, uint32_t opacity, uint32_t (*blendMethod)(uint32_t)) { + TVGLOG("SW_ENGINE", "Image Rle Alpha Mask / Inverse Alpha Mask Composition"); + auto span = image->rle->spans; - auto img = image->data; - auto w = image->w; - auto h = image->h; + auto cbuffer = surface->compositor->image.data; for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto ey1 = span->y * itransform->e12 + itransform->e13; - auto ey2 = span->y * itransform->e22 + itransform->e23; auto dst = &surface->buffer[span->y * surface->stride + span->x]; - for (uint32_t x = 0; x < span->len; ++x, ++dst) { - auto rX = static_cast(roundf((span->x + x) * itransform->e11 + ey1)); - auto rY = static_cast(roundf((span->x + 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[rY * image->stride + rX], span->coverage); - else src = ALPHA_BLEND(_average2Nx2NPixel(surface, img, image->stride, h, rX, rY, halfScale), span->coverage); - *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); + auto cmp = &cbuffer[span->y * surface->stride + span->x]; + auto img = image->data + (span->y + image->y) * image->stride + (span->x + image->x); + auto alpha = _multiplyAlpha(span->coverage, opacity); + if (alpha == 255) { + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++img) { + auto tmp = ALPHA_BLEND(*img, blendMethod(*cmp)); + *dst = tmp + ALPHA_BLEND(*dst, surface->blender.ialpha(tmp)); + } + } else { + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++img) { + auto tmp = ALPHA_BLEND(*img, _multiplyAlpha(alpha, blendMethod(*cmp))); + *dst = tmp + ALPHA_BLEND(*dst, surface->blender.ialpha(tmp)); + } } } return true; } -static bool _translucentImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const SwBBox& region, const Matrix* itransform) +static bool __rasterDirectTranslucentRleImage(SwSurface* surface, const SwImage* image, 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]; + auto span = image->rle->spans; - 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(roundf(x * itransform->e11 + ey1)); - auto rY = static_cast(roundf(x * itransform->e21 + ey2)); - if (rX >= w || rY >= h) continue; - auto src = ALPHA_BLEND(img[rX + (rY * image->stride)], opacity); + for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { + auto dst = &surface->buffer[span->y * surface->stride + span->x]; + auto img = image->data + (span->y + image->y) * image->stride + (span->x + image->x); + auto alpha = _multiplyAlpha(span->coverage, opacity); + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { + auto src = ALPHA_BLEND(*img, alpha); *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); } - dbuffer += surface->stride; } return true; } -static bool _translucentImageMask(SwSurface* surface, const SwImage* image, uint32_t opacity, const SwBBox& region, const Matrix* itransform, uint32_t (*blendMethod)(uint32_t)) +static bool _rasterDirectTranslucentRleImage(SwSurface* surface, const SwImage* image, uint32_t opacity) { - TVGLOG("SW_ENGINE", "Transformed Image AlphaMask / Inverse Alpha Mask Composition"); + if (surface->compositor) { + if (surface->compositor->method == CompositeMethod::AlphaMask) { + return _rasterDirectMaskedRleImage(surface, image, opacity, surface->blender.alpha); + } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { + return _rasterDirectMaskedRleImage(surface, image, opacity, surface->blender.ialpha); + } + } + return __rasterDirectTranslucentRleImage(surface, image, 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]; - auto cbuffer = &surface->compositor->image.data[region.min.y * surface->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(roundf(x * itransform->e11 + ey1)); - auto rY = static_cast(roundf(x * itransform->e21 + ey2)); - if (rX >= w || rY >= h) continue; - auto src = ALPHA_BLEND(img[rX + (rY * image->stride)], ALPHA_MULTIPLY(opacity, blendMethod(*cmp))); - *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); +static bool _rasterDirectSolidRleImage(SwSurface* surface, const SwImage* image) +{ + auto span = image->rle->spans; + + for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { + auto dst = &surface->buffer[span->y * surface->stride + span->x]; + auto img = image->data + (span->y + image->y) * image->stride + (span->x + image->x); + if (span->coverage == 255) { + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { + *dst = *img; + } + } else { + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { + auto src = ALPHA_BLEND(*img, span->coverage); + *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); + } } - dbuffer += surface->stride; - cbuffer += surface->stride; } return true; } -static bool _rasterTranslucentImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const SwBBox& region, const Matrix* itransform) +/************************************************************************/ +/* Whole Transformed Translucent Image */ +/************************************************************************/ + +static bool _rasterTransformedMaskedImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const SwBBox& region, const Matrix* itransform, uint32_t (*blendMethod)(uint32_t)) { - if (surface->compositor) { - if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _translucentImageMask(surface, image, opacity, region, itransform, surface->blender.alpha); - } - if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _translucentImageMask(surface, image, opacity, region, itransform, surface->blender.ialpha); + TVGLOG("SW_ENGINE", "Transformed Image AlphaMask / Inverse Alpha Mask Composition"); + + 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->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(roundf(x * itransform->e11 + ey1)); + auto rY = static_cast(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->stride; } - return _translucentImage(surface, image, opacity, region, itransform); + return true; } -static bool _translucentUpScaleImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const SwBBox& region, const Matrix* itransform) +static bool _rasterTransformedTranslucentImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const SwBBox& region, const Matrix* itransform) { auto img = image->data; auto w = image->w; @@ -723,14 +710,11 @@ static bool _translucentUpScaleImage(SwSurface* surface, const SwImage* image, u 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(roundf(fX)); - auto rY = static_cast(roundf(fY)); + auto rX = static_cast(roundf(x * itransform->e11 + ey1)); + auto rY = static_cast(roundf(x * itransform->e21 + ey2)); 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(_applyBilinearInterpolation(img, image->stride, h, fX, fY), opacity); + + auto src = ALPHA_BLEND(img[rX + (rY * image->stride)], opacity); *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); } dbuffer += surface->stride; @@ -739,7 +723,7 @@ static bool _translucentUpScaleImage(SwSurface* surface, const SwImage* image, u } -static bool _translucentUpScaleImageMask(SwSurface* surface, const SwImage* image, uint32_t opacity, const SwBBox& region, const Matrix* itransform, uint32_t (*blendMethod)(uint32_t)) +static bool _rasterDownScaledMaskedImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const SwBBox& region, const Matrix* itransform, uint32_t halfScale, uint32_t (*blendMethod)(uint32_t)) { TVGLOG("SW_ENGINE", "Transformed Image Alpha Mask / Inverse Alpha Mask Composition"); @@ -755,14 +739,15 @@ static bool _translucentUpScaleImageMask(SwSurface* surface, const SwImage* imag 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(roundf(fX)); - auto rY = static_cast(roundf(fY)); + auto rX = static_cast(roundf(x * itransform->e11 + ey1)); + auto rY = static_cast(roundf(x * itransform->e21 + ey2)); if (rX >= w || rY >= h) continue; uint32_t src; - if (rX == w - 1 || rY == h - 1) src = ALPHA_BLEND(img[rX + (rY * image->stride)], ALPHA_MULTIPLY(opacity, blendMethod(*cmp))); - else src = ALPHA_BLEND(_applyBilinearInterpolation(img, image->stride, h, fX, fY), ALPHA_MULTIPLY(opacity, blendMethod(*cmp))); + 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(_average2Nx2NPixel(surface, img, image->stride, h, rX, rY, halfScale), _multiplyAlpha(opacity, blendMethod(*cmp))); + } *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); } dbuffer += surface->stride; @@ -772,21 +757,7 @@ static bool _translucentUpScaleImageMask(SwSurface* surface, const SwImage* imag } -static bool _rasterTranslucentUpScaleImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const SwBBox& region, const Matrix* itransform) -{ - if (surface->compositor) { - if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _translucentUpScaleImageMask(surface, image, opacity, region, itransform, surface->blender.alpha); - } - if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _translucentUpScaleImageMask(surface, image, opacity, region, itransform, surface->blender.ialpha); - } - } - return _translucentUpScaleImage(surface, image, opacity, region, itransform); -} - - -static bool _translucentDownScaleImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const SwBBox& region, const Matrix* itransform, uint32_t halfScale) +static bool _rasterDownScaledTranslucentImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const SwBBox& region, const Matrix* itransform, uint32_t halfScale) { auto img = image->data; auto w = image->w; @@ -812,7 +783,7 @@ static bool _translucentDownScaleImage(SwSurface* surface, const SwImage* image, } -static bool _translucentDownScaleImageMask(SwSurface* surface, const SwImage* image, uint32_t opacity, const SwBBox& region, const Matrix* itransform, uint32_t halfScale, uint32_t (*blendMethod)(uint32_t)) +static bool _rasterUpScaledMaskedImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const SwBBox& region, const Matrix* itransform, uint32_t (*blendMethod)(uint32_t)) { TVGLOG("SW_ENGINE", "Transformed Image Alpha Mask / Inverse Alpha Mask Composition"); @@ -828,15 +799,14 @@ static bool _translucentDownScaleImageMask(SwSurface* surface, const SwImage* im 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(roundf(x * itransform->e11 + ey1)); - auto rY = static_cast(roundf(x * itransform->e21 + ey2)); + auto fX = x * itransform->e11 + ey1; + auto fY = x * itransform->e21 + ey2; + auto rX = static_cast(roundf(fX)); + auto rY = static_cast(roundf(fY)); 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)], ALPHA_MULTIPLY(opacity, blendMethod(*cmp))); - } else { - src = ALPHA_BLEND(_average2Nx2NPixel(surface, img, image->stride, h, rX, rY, halfScale), ALPHA_MULTIPLY(opacity, blendMethod(*cmp))); - } + if (rX == w - 1 || rY == h - 1) src = ALPHA_BLEND(img[rX + (rY * image->stride)], _multiplyAlpha(opacity, blendMethod(*cmp))); + else src = ALPHA_BLEND(_applyBilinearInterpolation(img, image->stride, h, fX, fY), _multiplyAlpha(opacity, blendMethod(*cmp))); *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); } dbuffer += surface->stride; @@ -846,99 +816,76 @@ static bool _translucentDownScaleImageMask(SwSurface* surface, const SwImage* im } -static bool _rasterTranslucentDownScaleImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const SwBBox& region, const Matrix* itransform, uint32_t halfScale) -{ - if (surface->compositor) { - if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _translucentDownScaleImageMask(surface, image, opacity, region, itransform, halfScale, surface->blender.alpha); - } - if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _translucentDownScaleImageMask(surface, image, opacity, region, itransform, halfScale, surface->blender.ialpha); - } - } - return _translucentDownScaleImage(surface, image, opacity, region, itransform, halfScale); -} - - -static bool _translucentImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const SwBBox& region) +static bool _rasterUpScaledTranslucentImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const SwBBox& region, const Matrix* itransform) { + 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 sbuffer = image->data + (region.min.y + image->y) * image->stride + (region.min.x + image->x); for (auto y = region.min.y; y < region.max.y; ++y) { auto dst = dbuffer; - auto src = sbuffer; - for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) { - auto p = ALPHA_BLEND(*src, opacity); - *dst = p + ALPHA_BLEND(*dst, surface->blender.ialpha(p)); + 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(roundf(fX)); + auto rY = static_cast(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(_applyBilinearInterpolation(img, image->stride, h, fX, fY), opacity); + *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); } dbuffer += surface->stride; - sbuffer += image->stride; } return true; } -static bool _translucentImageMask(SwSurface* surface, const SwImage* image, uint32_t opacity, const SwBBox& region, uint32_t (*blendMethod)(uint32_t)) +static bool _rasterTransformedTranslucentImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const SwBBox& region, const Matrix* itransform, uint32_t halfScale) { - auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x; - auto h2 = static_cast(region.max.y - region.min.y); - auto w2 = static_cast(region.max.x - region.min.x); - - TVGLOG("SW_ENGINE", "Image Alpha Mask / Inverse Alpha Mask Composition"); - - auto sbuffer = image->data + (region.min.y + image->y) * image->stride + (region.min.x + image->x); - auto cbuffer = surface->compositor->image.data + (region.min.y * surface->stride) + region.min.x; //compositor buffer - - for (uint32_t y = 0; y < h2; ++y) { - auto dst = buffer; - auto cmp = cbuffer; - auto src = sbuffer; - for (uint32_t x = 0; x < w2; ++x, ++dst, ++src, ++cmp) { - auto tmp = ALPHA_BLEND(*src, ALPHA_MULTIPLY(opacity, blendMethod(*cmp))); - *dst = tmp + ALPHA_BLEND(*dst, surface->blender.ialpha(tmp)); + //Transformd + if (mathEqual(image->scale, 1.0f)) { + if (surface->compositor) { + if (surface->compositor->method == CompositeMethod::AlphaMask) { + return _rasterTransformedMaskedImage(surface, image, opacity, region, itransform, surface->blender.alpha); + } + if (surface->compositor->method == CompositeMethod::InvAlphaMask) { + return _rasterTransformedMaskedImage(surface, image, opacity, region, itransform, surface->blender.ialpha); + } } - buffer += surface->stride; - cbuffer += surface->stride; - sbuffer += image->stride; - } - return true; -} - - -static bool _rasterTranslucentImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const SwBBox& region) -{ - if (surface->compositor) { - if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _translucentImageMask(surface, image, opacity, region, surface->blender.alpha); + return _rasterTransformedTranslucentImage(surface, image, opacity, region, itransform); + //Transformed + DownScaled + } else if (image->scale < DOWN_SCALE_TOLERANCE) { + if (surface->compositor) { + if (surface->compositor->method == CompositeMethod::AlphaMask) { + return _rasterDownScaledMaskedImage(surface, image, opacity, region, itransform, halfScale, surface->blender.alpha); + } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { + return _rasterDownScaledMaskedImage(surface, image, opacity, region, itransform, halfScale, surface->blender.ialpha); + } } - if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _translucentImageMask(surface, image, opacity, region, surface->blender.ialpha); + return _rasterDownScaledTranslucentImage(surface, image, opacity, region, itransform, halfScale); + //Transformed + UpScaled + } else { + if (surface->compositor) { + if (surface->compositor->method == CompositeMethod::AlphaMask) { + return _rasterUpScaledMaskedImage(surface, image, opacity, region, itransform, surface->blender.alpha); + }else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { + return _rasterUpScaledMaskedImage(surface, image, opacity, region, itransform, surface->blender.ialpha); + } } + return _rasterUpScaledTranslucentImage(surface, image, opacity, region, itransform); } - return _translucentImage(surface, image, opacity, region); } -static bool _rasterImage(SwSurface* surface, const SwImage* image, const SwBBox& region) -{ - auto dbuffer = &surface->buffer[region.min.y * surface->stride + region.min.x]; - auto sbuffer = image->data + (region.min.y + image->y) * image->stride + (region.min.x + image->x); - - for (auto y = region.min.y; y < region.max.y; ++y) { - auto dst = dbuffer; - auto src = sbuffer; - for (auto x = region.min.x; x < region.max.x; x++, dst++, src++) { - *dst = *src + ALPHA_BLEND(*dst, surface->blender.ialpha(*src)); - } - dbuffer += surface->stride; - sbuffer += image->stride; - } - return true; -} - +/************************************************************************/ +/* Whole Transformed Solid Image */ +/************************************************************************/ -static bool _rasterImage(SwSurface* surface, const SwImage* image, const SwBBox& region, const Matrix* itransform) +static bool _rasterTransformedSolidImage(SwSurface* surface, const SwImage* image, const SwBBox& region, const Matrix* itransform) { auto img = image->data; auto w = image->w; @@ -960,7 +907,7 @@ static bool _rasterImage(SwSurface* surface, const SwImage* image, const SwBBox& } -static bool _rasterUpScaleImage(SwSurface* surface, const SwImage* image, const SwBBox& region, const Matrix* itransform) +static bool _rasterDownScaledSolidImage(SwSurface* surface, const SwImage* image, const SwBBox& region, const Matrix* itransform, uint32_t halfScale) { auto img = image->data; auto w = image->w; @@ -971,14 +918,12 @@ static bool _rasterUpScaleImage(SwSurface* surface, const SwImage* image, const 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(roundf(fX)); - auto rY = static_cast(roundf(fY)); + auto rX = static_cast(roundf(x * itransform->e11 + ey1)); + auto rY = static_cast(roundf(x * itransform->e21 + ey2)); if (rX >= w || rY >= h) continue; uint32_t src; - if (rX == w - 1 || rY == h - 1) src = img[rX + (rY * w)]; - else src = _applyBilinearInterpolation(img, w, h, fX, fY); + if (rX < halfScale || rY < halfScale || rX >= w - halfScale || rY >= h - halfScale) src = img[rX + (rY * w)]; + else src = _average2Nx2NPixel(surface, img, w, h, rX, rY, halfScale); *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); } } @@ -986,26 +931,25 @@ static bool _rasterUpScaleImage(SwSurface* surface, const SwImage* image, const } -static bool _rasterDownScaleImage(SwSurface* surface, const SwImage* image, const SwBBox& region, const Matrix* itransform, float scale) +static bool _rasterUpScaledSolidImage(SwSurface* surface, const SwImage* image, const SwBBox& region, const Matrix* itransform) { auto img = image->data; auto w = image->w; auto h = image->h; - auto halfScale = static_cast(0.5f / scale); - if (halfScale == 0) halfScale = 1; - 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(roundf(x * itransform->e11 + ey1)); - auto rY = static_cast(roundf(x * itransform->e21 + ey2)); + auto fX = x * itransform->e11 + ey1; + auto fY = x * itransform->e21 + ey2; + auto rX = static_cast(roundf(fX)); + auto rY = static_cast(roundf(fY)); 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 = _average2Nx2NPixel(surface, img, w, h, rX, rY, halfScale); + if (rX == w - 1 || rY == h - 1) src = img[rX + (rY * w)]; + else src = _applyBilinearInterpolation(img, w, h, fX, fY); *dst = src + ALPHA_BLEND(*dst, surface->blender.ialpha(src)); } } @@ -1013,190 +957,209 @@ static bool _rasterDownScaleImage(SwSurface* surface, const SwImage* image, cons } +static bool _rasterTransformedSolidImage(SwSurface* surface, const SwImage* image, const SwBBox& region, const Matrix* itransform, uint32_t halfScale) +{ + if (mathEqual(image->scale, 1.0f)) return _rasterTransformedSolidImage(surface, image, region, itransform); + else if (image->scale < DOWN_SCALE_TOLERANCE) return _rasterDownScaledSolidImage(surface, image, region, itransform, halfScale); + else return _rasterUpScaledSolidImage(surface, image, region, itransform); +} + + /************************************************************************/ -/* Gradient */ +/* Whole (Solid + Translucent) Direct Image */ /************************************************************************/ -static bool _translucentLinearGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) +static bool _rasterDirectMaskedImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const SwBBox& region, uint32_t (*blendMethod)(uint32_t)) { - if (fill->linear.len < FLT_EPSILON) return false; - auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x; - auto h = static_cast(region.max.y - region.min.y); - auto w = static_cast(region.max.x - region.min.x); + auto h2 = static_cast(region.max.y - region.min.y); + auto w2 = static_cast(region.max.x - region.min.x); - auto sbuffer = static_cast(alloca(w * sizeof(uint32_t))); - if (!sbuffer) return false; + TVGLOG("SW_ENGINE", "Image Alpha Mask / Inverse Alpha Mask Composition"); - auto dst = buffer; - for (uint32_t y = 0; y < h; ++y) { - fillFetchLinear(fill, sbuffer, region.min.y + y, region.min.x, w); - for (uint32_t x = 0; x < w; ++x) { - dst[x] = sbuffer[x] + ALPHA_BLEND(dst[x], surface->blender.ialpha(sbuffer[x])); + auto sbuffer = image->data + (region.min.y + image->y) * image->stride + (region.min.x + image->x); + auto cbuffer = surface->compositor->image.data + (region.min.y * surface->stride) + region.min.x; //compositor buffer + + for (uint32_t y = 0; y < h2; ++y) { + auto dst = buffer; + auto cmp = cbuffer; + auto src = sbuffer; + for (uint32_t x = 0; x < w2; ++x, ++dst, ++src, ++cmp) { + auto tmp = ALPHA_BLEND(*src, _multiplyAlpha(opacity, blendMethod(*cmp))); + *dst = tmp + ALPHA_BLEND(*dst, surface->blender.ialpha(tmp)); } - dst += surface->stride; + buffer += surface->stride; + cbuffer += surface->stride; + sbuffer += image->stride; } return true; } -static bool _translucentLinearGradientRectMask(SwSurface* surface, const SwBBox& region, const SwFill* fill, uint32_t (*blendMethod)(uint32_t)) +static bool __rasterDirectTranslucentImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const SwBBox& region) { - if (fill->linear.len < FLT_EPSILON) return false; - - auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x; - auto h = static_cast(region.max.y - region.min.y); - auto w = static_cast(region.max.x - region.min.x); - auto cbuffer = surface->compositor->image.data + (region.min.y * surface->stride) + region.min.x; - - auto sbuffer = static_cast(alloca(w * sizeof(uint32_t))); - if (!sbuffer) return false; + auto dbuffer = &surface->buffer[region.min.y * surface->stride + region.min.x]; + auto sbuffer = image->data + (region.min.y + image->y) * image->stride + (region.min.x + image->x); - for (uint32_t y = 0; y < h; ++y) { - fillFetchLinear(fill, sbuffer, region.min.y + y, region.min.x, w); - auto dst = buffer; - auto cmp = cbuffer; + for (auto y = region.min.y; y < region.max.y; ++y) { + auto dst = dbuffer; auto src = sbuffer; - for (uint32_t x = 0; x < w; ++x, ++dst, ++cmp, ++src) { - auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp)); - *dst = tmp + ALPHA_BLEND(*dst, surface->blender.ialpha(tmp)); + for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) { + auto p = ALPHA_BLEND(*src, opacity); + *dst = p + ALPHA_BLEND(*dst, surface->blender.ialpha(p)); } - buffer += surface->stride; - cbuffer += surface->stride; + dbuffer += surface->stride; + sbuffer += image->stride; } return true; } -static bool _rasterTranslucentLinearGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) +static bool _rasterDirectTranslucentImage(SwSurface* surface, const SwImage* image, uint32_t opacity, const SwBBox& region) { if (surface->compositor) { if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _translucentLinearGradientRectMask(surface, region, fill, surface->blender.alpha); - } - if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _translucentLinearGradientRectMask(surface, region, fill, surface->blender.ialpha); + return _rasterDirectMaskedImage(surface, image, opacity, region, surface->blender.alpha); + } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { + return _rasterDirectMaskedImage(surface, image, opacity, region, surface->blender.ialpha); } } - return _translucentLinearGradientRect(surface, region, fill); + return __rasterDirectTranslucentImage(surface, image, opacity, region); } -static bool _rasterOpaqueLinearGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) +static bool _rasterDirectSolidImage(SwSurface* surface, const SwImage* image, const SwBBox& region) { - if (fill->linear.len < FLT_EPSILON) return false; - - auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x; - auto h = static_cast(region.max.y - region.min.y); - auto w = static_cast(region.max.x - region.min.x); + auto dbuffer = &surface->buffer[region.min.y * surface->stride + region.min.x]; + auto sbuffer = image->data + (region.min.y + image->y) * image->stride + (region.min.x + image->x); - for (uint32_t y = 0; y < h; ++y) { - fillFetchLinear(fill, buffer + y * surface->stride, region.min.y + y, region.min.x, w); + for (auto y = region.min.y; y < region.max.y; ++y) { + auto dst = dbuffer; + auto src = sbuffer; + for (auto x = region.min.x; x < region.max.x; x++, dst++, src++) { + *dst = *src + ALPHA_BLEND(*dst, surface->blender.ialpha(*src)); + } + dbuffer += surface->stride; + sbuffer += image->stride; } return true; } -static bool _translucentRadialGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) +/************************************************************************/ +/* Rect Linear Gradient */ +/************************************************************************/ + +static bool _rasterTranslucentLinearGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill, uint32_t (*blendMethod)(uint32_t)) { - if (fill->radial.a < FLT_EPSILON) return false; + if (fill->linear.len < FLT_EPSILON) return false; auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x; auto h = static_cast(region.max.y - region.min.y); auto w = static_cast(region.max.x - region.min.x); + auto cbuffer = surface->compositor->image.data + (region.min.y * surface->stride) + region.min.x; auto sbuffer = static_cast(alloca(w * sizeof(uint32_t))); if (!sbuffer) return false; - auto dst = buffer; for (uint32_t y = 0; y < h; ++y) { - fillFetchRadial(fill, sbuffer, region.min.y + y, region.min.x, w); - for (uint32_t x = 0; x < w; ++x) { - dst[x] = sbuffer[x] + ALPHA_BLEND(dst[x], surface->blender.ialpha(sbuffer[x])); + fillFetchLinear(fill, sbuffer, region.min.y + y, region.min.x, w); + auto dst = buffer; + auto cmp = cbuffer; + auto src = sbuffer; + for (uint32_t x = 0; x < w; ++x, ++dst, ++cmp, ++src) { + auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp)); + *dst = tmp + ALPHA_BLEND(*dst, surface->blender.ialpha(tmp)); } - dst += surface->stride; + buffer += surface->stride; + cbuffer += surface->stride; } return true; } -static bool _translucentRadialGradientRectMask(SwSurface* surface, const SwBBox& region, const SwFill* fill, uint32_t (*blendMethod)(uint32_t)) +static bool __rasterTranslucentLinearGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) { - if (fill->radial.a < FLT_EPSILON) return false; + if (fill->linear.len < FLT_EPSILON) return false; auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x; auto h = static_cast(region.max.y - region.min.y); auto w = static_cast(region.max.x - region.min.x); - auto cbuffer = surface->compositor->image.data + (region.min.y * surface->stride) + region.min.x; auto sbuffer = static_cast(alloca(w * sizeof(uint32_t))); if (!sbuffer) return false; + auto dst = buffer; for (uint32_t y = 0; y < h; ++y) { - fillFetchRadial(fill, sbuffer, region.min.y + y, region.min.x, w); - auto dst = buffer; - auto cmp = cbuffer; - auto src = sbuffer; - for (uint32_t x = 0; x < w; ++x, ++dst, ++cmp, ++src) { - auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp)); - *dst = tmp + ALPHA_BLEND(*dst, surface->blender.ialpha(tmp)); + fillFetchLinear(fill, sbuffer, region.min.y + y, region.min.x, w); + for (uint32_t x = 0; x < w; ++x) { + dst[x] = sbuffer[x] + ALPHA_BLEND(dst[x], surface->blender.ialpha(sbuffer[x])); } - buffer += surface->stride; - cbuffer += surface->stride; + dst += surface->stride; } return true; } -static bool _rasterTranslucentRadialGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) +static bool _rasterTranslucentLinearGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) { if (surface->compositor) { if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _translucentRadialGradientRectMask(surface, region, fill, surface->blender.alpha); + return _rasterTranslucentLinearGradientMaskedRect(surface, region, fill, surface->blender.alpha); } if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _translucentRadialGradientRectMask(surface, region, fill, surface->blender.ialpha); + return _rasterTranslucentLinearGradientMaskedRect(surface, region, fill, surface->blender.ialpha); } } - return _translucentRadialGradientRect(surface, region, fill); + return __rasterTranslucentLinearGradientRect(surface, region, fill); } -static bool _rasterOpaqueRadialGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) +static bool _rasterSolidLinearGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) { - if (fill->radial.a < FLT_EPSILON) return false; + if (fill->linear.len < FLT_EPSILON) return false; auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x; auto h = static_cast(region.max.y - region.min.y); auto w = static_cast(region.max.x - region.min.x); for (uint32_t y = 0; y < h; ++y) { - auto dst = &buffer[y * surface->stride]; - fillFetchRadial(fill, dst, region.min.y + y, region.min.x, w); + fillFetchLinear(fill, buffer + y * surface->stride, region.min.y + y, region.min.x, w); } return true; } -static bool _translucentLinearGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) +/************************************************************************/ +/* Rle Linear Gradient */ +/************************************************************************/ + + +static bool _rasterTranslucentLinearGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, uint32_t (*blendMethod)(uint32_t)) { if (fill->linear.len < FLT_EPSILON) return false; auto span = rle->spans; + auto cbuffer = surface->compositor->image.data; auto buffer = static_cast(alloca(surface->w * sizeof(uint32_t))); if (!buffer) return false; for (uint32_t i = 0; i < rle->size; ++i, ++span) { - auto dst = &surface->buffer[span->y * surface->stride + span->x]; fillFetchLinear(fill, buffer, span->y, span->x, span->len); + auto dst = &surface->buffer[span->y * surface->stride + span->x]; + auto cmp = &cbuffer[span->y * surface->stride + span->x]; + auto src = buffer; if (span->coverage == 255) { - for (uint32_t i = 0; i < span->len; ++i) { - dst[i] = buffer[i] + ALPHA_BLEND(dst[i], surface->blender.ialpha(buffer[i])); + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++src) { + auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp)); + *dst = tmp + ALPHA_BLEND(*dst, surface->blender.ialpha(tmp)); } } else { - for (uint32_t i = 0; i < span->len; ++i) { - auto tmp = ALPHA_BLEND(buffer[i], span->coverage); - dst[i] = tmp + ALPHA_BLEND(dst[i], surface->blender.ialpha(tmp)); + auto ialpha = 255 - span->coverage; + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++src) { + auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp)); + tmp = ALPHA_BLEND(tmp, span->coverage) + ALPHA_BLEND(*dst, ialpha); + *dst = tmp + ALPHA_BLEND(*dst, surface->blender.ialpha(tmp)); } } } @@ -1204,31 +1167,25 @@ static bool _translucentLinearGradientRle(SwSurface* surface, const SwRleData* r } -static bool _translucentLinearGradientRleMask(SwSurface* surface, const SwRleData* rle, const SwFill* fill, uint32_t (*blendMethod)(uint32_t)) +static bool __rasterTranslucentLinearGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) { if (fill->linear.len < FLT_EPSILON) return false; auto span = rle->spans; - auto cbuffer = surface->compositor->image.data; auto buffer = static_cast(alloca(surface->w * sizeof(uint32_t))); if (!buffer) return false; for (uint32_t i = 0; i < rle->size; ++i, ++span) { - fillFetchLinear(fill, buffer, span->y, span->x, span->len); auto dst = &surface->buffer[span->y * surface->stride + span->x]; - auto cmp = &cbuffer[span->y * surface->stride + span->x]; - auto src = buffer; + fillFetchLinear(fill, buffer, span->y, span->x, span->len); if (span->coverage == 255) { - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++src) { - auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp)); - *dst = tmp + ALPHA_BLEND(*dst, surface->blender.ialpha(tmp)); + for (uint32_t i = 0; i < span->len; ++i) { + dst[i] = buffer[i] + ALPHA_BLEND(dst[i], surface->blender.ialpha(buffer[i])); } } else { - auto ialpha = 255 - span->coverage; - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++src) { - auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp)); - tmp = ALPHA_BLEND(tmp, span->coverage) + ALPHA_BLEND(*dst, ialpha); - *dst = tmp + ALPHA_BLEND(*dst, surface->blender.ialpha(tmp)); + for (uint32_t i = 0; i < span->len; ++i) { + auto tmp = ALPHA_BLEND(buffer[i], span->coverage); + dst[i] = tmp + ALPHA_BLEND(dst[i], surface->blender.ialpha(tmp)); } } } @@ -1242,17 +1199,16 @@ static bool _rasterTranslucentLinearGradientRle(SwSurface* surface, const SwRleD if (surface->compositor) { if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _translucentLinearGradientRleMask(surface, rle, fill, surface->blender.alpha); - } - if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _translucentLinearGradientRleMask(surface, rle, fill, surface->blender.ialpha); + return _rasterTranslucentLinearGradientMaskedRle(surface, rle, fill, surface->blender.alpha); + } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { + return _rasterTranslucentLinearGradientMaskedRle(surface, rle, fill, surface->blender.ialpha); } } - return _translucentLinearGradientRle(surface, rle, fill); + return __rasterTranslucentLinearGradientRle(surface, rle, fill); } -static bool _rasterOpaqueLinearGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) +static bool _rasterSolidLinearGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) { if (fill->linear.len < FLT_EPSILON) return false; @@ -1277,33 +1233,96 @@ static bool _rasterOpaqueLinearGradientRle(SwSurface* surface, const SwRleData* } -static bool _translucentRadialGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) +/************************************************************************/ +/* Rect Radial Gradient */ +/************************************************************************/ + +static bool _rasterTranslucentRadialGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill, uint32_t (*blendMethod)(uint32_t)) { if (fill->radial.a < FLT_EPSILON) return false; - auto span = rle->spans; - auto buffer = static_cast(alloca(surface->w * sizeof(uint32_t))); - if (!buffer) return false; + auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x; + auto h = static_cast(region.max.y - region.min.y); + auto w = static_cast(region.max.x - region.min.x); + auto cbuffer = surface->compositor->image.data + (region.min.y * surface->stride) + region.min.x; - for (uint32_t i = 0; i < rle->size; ++i, ++span) { - auto dst = &surface->buffer[span->y * surface->stride + span->x]; - fillFetchRadial(fill, buffer, span->y, span->x, span->len); - if (span->coverage == 255) { - for (uint32_t i = 0; i < span->len; ++i) { - dst[i] = buffer[i] + ALPHA_BLEND(dst[i], surface->blender.ialpha(buffer[i])); - } - } else { - for (uint32_t i = 0; i < span->len; ++i) { - auto tmp = ALPHA_BLEND(buffer[i], span->coverage); - dst[i] = tmp + ALPHA_BLEND(dst[i], surface->blender.ialpha(tmp)); - } + auto sbuffer = static_cast(alloca(w * sizeof(uint32_t))); + if (!sbuffer) return false; + + for (uint32_t y = 0; y < h; ++y) { + fillFetchRadial(fill, sbuffer, region.min.y + y, region.min.x, w); + auto dst = buffer; + auto cmp = cbuffer; + auto src = sbuffer; + for (uint32_t x = 0; x < w; ++x, ++dst, ++cmp, ++src) { + auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp)); + *dst = tmp + ALPHA_BLEND(*dst, surface->blender.ialpha(tmp)); } + buffer += surface->stride; + cbuffer += surface->stride; } return true; } -static bool _translucentRadialGradientRleMask(SwSurface* surface, const SwRleData* rle, const SwFill* fill, uint32_t (*blendMethod)(uint32_t)) +static bool __rasterTranslucentRadialGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) +{ + if (fill->radial.a < FLT_EPSILON) return false; + + auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x; + auto h = static_cast(region.max.y - region.min.y); + auto w = static_cast(region.max.x - region.min.x); + + auto sbuffer = static_cast(alloca(w * sizeof(uint32_t))); + if (!sbuffer) return false; + + auto dst = buffer; + for (uint32_t y = 0; y < h; ++y) { + fillFetchRadial(fill, sbuffer, region.min.y + y, region.min.x, w); + for (uint32_t x = 0; x < w; ++x) { + dst[x] = sbuffer[x] + ALPHA_BLEND(dst[x], surface->blender.ialpha(sbuffer[x])); + } + dst += surface->stride; + } + return true; +} + + +static bool _rasterTranslucentRadialGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) +{ + if (surface->compositor) { + if (surface->compositor->method == CompositeMethod::AlphaMask) { + return _rasterTranslucentRadialGradientMaskedRect(surface, region, fill, surface->blender.alpha); + } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { + return _rasterTranslucentRadialGradientMaskedRect(surface, region, fill, surface->blender.ialpha); + } + } + return __rasterTranslucentRadialGradientRect(surface, region, fill); +} + + +static bool _rasterSolidRadialGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) +{ + if (fill->radial.a < FLT_EPSILON) return false; + + auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x; + auto h = static_cast(region.max.y - region.min.y); + auto w = static_cast(region.max.x - region.min.x); + + for (uint32_t y = 0; y < h; ++y) { + auto dst = &buffer[y * surface->stride]; + fillFetchRadial(fill, dst, region.min.y + y, region.min.x, w); + } + return true; +} + + +/************************************************************************/ +/* RLE Radial Gradient */ +/************************************************************************/ + + +static bool _rasterTranslucentRadialGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, uint32_t (*blendMethod)(uint32_t)) { if (fill->radial.a < FLT_EPSILON) return false; @@ -1335,23 +1354,48 @@ static bool _translucentRadialGradientRleMask(SwSurface* surface, const SwRleDat } +static bool __rasterTranslucentRadialGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) +{ + if (fill->radial.a < FLT_EPSILON) return false; + + auto span = rle->spans; + auto buffer = static_cast(alloca(surface->w * sizeof(uint32_t))); + if (!buffer) return false; + + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + auto dst = &surface->buffer[span->y * surface->stride + span->x]; + fillFetchRadial(fill, buffer, span->y, span->x, span->len); + if (span->coverage == 255) { + for (uint32_t i = 0; i < span->len; ++i) { + dst[i] = buffer[i] + ALPHA_BLEND(dst[i], surface->blender.ialpha(buffer[i])); + } + } else { + for (uint32_t i = 0; i < span->len; ++i) { + auto tmp = ALPHA_BLEND(buffer[i], span->coverage); + dst[i] = tmp + ALPHA_BLEND(dst[i], surface->blender.ialpha(tmp)); + } + } + } + return true; +} + + static bool _rasterTranslucentRadialGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) { if (!rle) return false; if (surface->compositor) { if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _translucentRadialGradientRleMask(surface, rle, fill, surface->blender.alpha); - } - if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _translucentRadialGradientRleMask(surface, rle, fill, surface->blender.ialpha); + return _rasterTranslucentRadialGradientMaskedRle(surface, rle, fill, surface->blender.alpha); + } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { + return _rasterTranslucentRadialGradientMaskedRle(surface, rle, fill, surface->blender.ialpha); } } - return _translucentRadialGradientRle(surface, rle, fill); + return __rasterTranslucentRadialGradientRle(surface, rle, fill); } -static bool _rasterOpaqueRadialGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) +static bool _rasterSolidRadialGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) { if (fill->radial.a < FLT_EPSILON) return false; @@ -1419,19 +1463,19 @@ bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id) if (shape->fastTrack) { if (id == TVG_CLASS_ID_LINEAR) { if (translucent) return _rasterTranslucentLinearGradientRect(surface, shape->bbox, shape->fill); - return _rasterOpaqueLinearGradientRect(surface, shape->bbox, shape->fill); + return _rasterSolidLinearGradientRect(surface, shape->bbox, shape->fill); } else { if (translucent) return _rasterTranslucentRadialGradientRect(surface, shape->bbox, shape->fill); - return _rasterOpaqueRadialGradientRect(surface, shape->bbox, shape->fill); + return _rasterSolidRadialGradientRect(surface, shape->bbox, shape->fill); } } else { if (!shape->rle) return false; if (id == TVG_CLASS_ID_LINEAR) { if (translucent) return _rasterTranslucentLinearGradientRle(surface, shape->rle, shape->fill); - return _rasterOpaqueLinearGradientRle(surface, shape->rle, shape->fill); + return _rasterSolidLinearGradientRle(surface, shape->rle, shape->fill); } else { if (translucent) return _rasterTranslucentRadialGradientRle(surface, shape->rle, shape->fill); - return _rasterOpaqueRadialGradientRle(surface, shape->rle, shape->fill); + return _rasterSolidRadialGradientRle(surface, shape->rle, shape->fill); } } return false; @@ -1441,9 +1485,9 @@ bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id) bool rasterSolidShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { if (a < 255) { - r = ALPHA_MULTIPLY(r, a); - g = ALPHA_MULTIPLY(g, a); - b = ALPHA_MULTIPLY(b, a); + r = _multiplyAlpha(r, a); + g = _multiplyAlpha(g, a); + b = _multiplyAlpha(b, a); } auto color = surface->blender.join(r, g, b, a); @@ -1464,9 +1508,9 @@ bool rasterSolidShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { if (a < 255) { - r = ALPHA_MULTIPLY(r, a); - g = ALPHA_MULTIPLY(g, a); - b = ALPHA_MULTIPLY(b, a); + r = _multiplyAlpha(r, a); + g = _multiplyAlpha(g, a); + b = _multiplyAlpha(b, a); } auto color = surface->blender.join(r, g, b, a); @@ -1485,10 +1529,10 @@ bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id) if (id == TVG_CLASS_ID_LINEAR) { if (translucent) return _rasterTranslucentLinearGradientRle(surface, shape->strokeRle, shape->stroke->fill); - return _rasterOpaqueLinearGradientRle(surface, shape->strokeRle, shape->stroke->fill); + return _rasterSolidLinearGradientRle(surface, shape->strokeRle, shape->stroke->fill); } else { if (translucent) return _rasterTranslucentRadialGradientRle(surface, shape->strokeRle, shape->stroke->fill); - return _rasterOpaqueRadialGradientRle(surface, shape->strokeRle, shape->stroke->fill); + return _rasterSolidRadialGradientRle(surface, shape->strokeRle, shape->stroke->fill); } return false; @@ -1537,9 +1581,10 @@ void rasterUnpremultiply(SwSurface* surface) bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& bbox, uint32_t opacity) { - static constexpr float DOWN_SCALE_TOLERANCE = 0.5f; + Matrix itransform; + if (transform && !mathInverse(transform, &itransform)) return false; - uint32_t halfScale = static_cast(0.5f / image->scale); + auto halfScale = static_cast(0.5f / image->scale); if (halfScale == 0) halfScale = 1; auto translucent = _translucent(surface, opacity); @@ -1547,40 +1592,20 @@ bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, co //Clipped Image if (image->rle) { if (image->transformed) { - Matrix itransform; - if (!mathInverse(transform, &itransform)) return false; - if (translucent) { - if (mathEqual(image->scale, 1.0f)) return _rasterTranslucentImageRle(surface, image, opacity, &itransform); - else if (image->scale < DOWN_SCALE_TOLERANCE) return _rasterTranslucentDownScaleImageRle(surface, image, opacity, &itransform, halfScale); - else return _rasterTranslucentUpScaleImageRle(surface, image, opacity, &itransform); - } else { - if (mathEqual(image->scale, 1.0f)) return _rasterImageRle(surface, image, &itransform); - else if (image->scale < DOWN_SCALE_TOLERANCE) return _rasterDownScaleImageRle(surface, image, &itransform, halfScale); - else return _rasterUpScaleImageRle(surface, image, &itransform); - } - //Fast track: Only shifted image can go into this routine. + if (translucent) return _rasterTransformedTranslucentRleImage(surface, image, opacity, &itransform, halfScale); + else return _rasterTransformedSolidRleImage(surface, image, &itransform, halfScale); } else { - if (translucent) return _rasterTranslucentImageRle(surface, image, opacity); - return _rasterImageRle(surface, image); + if (translucent) return _rasterDirectTranslucentRleImage(surface, image, opacity); + else return _rasterDirectSolidRleImage(surface, image); } //Whole Image } else { if (image->transformed) { - Matrix itransform; - if (!mathInverse(transform, &itransform)) return false; - if (translucent) { - if (mathEqual(image->scale, 1.0f)) return _rasterTranslucentImage(surface, image, opacity, bbox, &itransform); - else if (image->scale < DOWN_SCALE_TOLERANCE) return _rasterTranslucentDownScaleImage(surface, image, opacity, bbox, &itransform, halfScale); - else return _rasterTranslucentUpScaleImage(surface, image, opacity, bbox, &itransform); - } else { - if (mathEqual(image->scale, 1.0f)) return _rasterImage(surface, image, bbox, &itransform); - else if (image->scale < DOWN_SCALE_TOLERANCE) return _rasterDownScaleImage(surface, image, bbox, &itransform, halfScale); - else return _rasterUpScaleImage(surface, image, bbox, &itransform); - } - //Fast track: Only shifted image can go into this routine. + if (translucent) return _rasterTransformedTranslucentImage(surface, image, opacity, bbox, &itransform, halfScale); + else return _rasterTransformedSolidImage(surface, image, bbox, &itransform, halfScale); } else { - if (translucent) return _rasterTranslucentImage(surface, image, opacity, bbox); - return _rasterImage(surface, image, bbox); + if (translucent) return _rasterDirectTranslucentImage(surface, image, opacity, bbox); + else return _rasterDirectSolidImage(surface, image, bbox); } } -} +} \ No newline at end of file -- 2.7.4 From 0444b48cae51c3d6660139d4c40b7cb88f5e404b Mon Sep 17 00:00:00 2001 From: jykeon Date: Thu, 18 Nov 2021 08:46:19 +0900 Subject: [PATCH 15/16] bump up version v0.5.4 Change-Id: I1b8b9e0dddd20c661d2e8736eaf7ce9ce9b08e67 Signed-off-by: jykeon --- packaging/thorvg.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/thorvg.spec b/packaging/thorvg.spec index 55c8183..1d3b3a3 100644 --- a/packaging/thorvg.spec +++ b/packaging/thorvg.spec @@ -1,6 +1,6 @@ Name: thorvg Summary: Thor Vector Graphics Library -Version: 0.5.3 +Version: 0.5.4 Release: 1 Group: Graphics System/Rendering Engine License: MIT -- 2.7.4 From 3b5948f3446eee943f85222d45c29a3ba1c410e1 Mon Sep 17 00:00:00 2001 From: jykeon Date: Thu, 18 Nov 2021 12:27:32 +0900 Subject: [PATCH 16/16] bump up version 0.6.0 Change-Id: I75ad4b7c3a0e98c811153726d25ec9004784c59d Signed-off-by: jykeon --- packaging/thorvg.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/thorvg.spec b/packaging/thorvg.spec index 1d3b3a3..8d423be 100644 --- a/packaging/thorvg.spec +++ b/packaging/thorvg.spec @@ -1,6 +1,6 @@ Name: thorvg Summary: Thor Vector Graphics Library -Version: 0.5.4 +Version: 0.6.0 Release: 1 Group: Graphics System/Rendering Engine License: MIT -- 2.7.4