From 31f70fb7a1dd1e34d45a7877a1ffb5bca1e012a0 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Sat, 28 Jan 2023 11:36:54 +0900 Subject: [PATCH 01/16] common: enhance clipping behavior. If a paint is used as a clipper, it must be determined in the paint behavior. Propagate its decision to the immediate derived classes so that not only shapes but also scenes must be dealt as a clipper properly. This revised this change 0de3872be33793d2c8db03d5b85da38670410626 for better a solution. Change-Id: I5525cc3ce952b74eec2a52f3bdc769f5ed600f58 --- src/lib/sw_engine/tvgSwRenderer.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index 48787a3..93d0d5d 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -77,10 +77,8 @@ struct SwShapeTask : SwTask bool clipper = false; void run(unsigned tid) override - { - auto compMethod = CompositeMethod::None; - auto usedAsClip = (sdata->composite(nullptr, &compMethod) == Result::Success) && (compMethod == CompositeMethod::ClipPath); - if (opacity == 0 && !usedAsClip) return; //Invisible + { + if (opacity == 0 && !clipper) return; //Invisible uint8_t strokeAlpha = 0; auto visibleStroke = false; @@ -102,7 +100,7 @@ struct SwShapeTask : SwTask sdata->fillColor(nullptr, nullptr, nullptr, &alpha); alpha = static_cast(static_cast(alpha) * opacity / 255); visibleFill = (alpha > 0 || sdata->fill()); - if (visibleFill || visibleStroke || usedAsClip) { + if (visibleFill || visibleStroke || clipper) { shapeReset(&shape); if (!shapePrepare(&shape, sdata, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) goto err; } @@ -114,7 +112,7 @@ struct SwShapeTask : SwTask //Fill if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) { - if (visibleFill || usedAsClip) { + if (visibleFill || clipper) { /* We assume that if stroke width is bigger than 2, shape outline below stroke could be full covered by stroke drawing. Thus it turns off antialising in that condition. -- 2.7.4 From f26e2ee6c0b6864f10fcd714d29a974f3b454bfa Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Sat, 28 Jan 2023 11:44:40 +0900 Subject: [PATCH 02/16] common paint: keep clean apis and small size. these are no more necessary. Change-Id: I315b39c77c413da1bc6102cabd5d797a1931eb9b --- inc/thorvg.h | 14 -------------- src/lib/tvgPaint.cpp | 13 ------------- src/lib/tvgPaint.h | 2 -- 3 files changed, 29 deletions(-) diff --git a/inc/thorvg.h b/inc/thorvg.h index c5a140f..1fd0f90 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -366,20 +366,6 @@ public: CompositeMethod composite(const Paint** target) const noexcept; /** - * @brief Gets the composition source object and the composition method. - * - * @param[out] source The paint of the composition source object. - * @param[out] method The method used to composite the source object with the target. - * - * @return Result::Success when the paint object used as a composition target, Result::InsufficientCondition otherwise. - * - * @warning Please do not use it, this API is not official one. It could be modified in the next version. - * - * @BETA_API - */ - Result composite(const Paint** source, CompositeMethod* method) const noexcept; - - /** * @brief Return the unique id value of the paint instance. * * This method can be called for checking the current concrete instance type. diff --git a/src/lib/tvgPaint.cpp b/src/lib/tvgPaint.cpp index 0b49122..506fc55 100644 --- a/src/lib/tvgPaint.cpp +++ b/src/lib/tvgPaint.cpp @@ -386,19 +386,6 @@ CompositeMethod Paint::composite(const Paint** target) const noexcept } -Result Paint::composite(const Paint** source, CompositeMethod* method) const noexcept -{ - if (source) *source = pImpl->compSource; - auto met = (pImpl->compSource && pImpl->compSource->pImpl->compData ? - pImpl->compSource->pImpl->compData->method : CompositeMethod::None); - if (method) *method = met; - - if (pImpl->compSource != nullptr && met != CompositeMethod::None) - return Result::Success; - return Result::InsufficientCondition; -} - - Result Paint::opacity(uint8_t o) noexcept { if (pImpl->opacity == o) return Result::Success; diff --git a/src/lib/tvgPaint.h b/src/lib/tvgPaint.h index 45c679d..9ec0232 100644 --- a/src/lib/tvgPaint.h +++ b/src/lib/tvgPaint.h @@ -63,7 +63,6 @@ namespace tvg StrategyMethod* smethod = nullptr; RenderTransform* rTransform = nullptr; Composite* compData = nullptr; - Paint* compSource = nullptr; uint32_t renderFlag = RenderUpdateFlag::None; uint32_t ctxFlag = ContextFlag::Invalid; uint32_t id; @@ -138,7 +137,6 @@ namespace tvg if (!target && method == CompositeMethod::None) return true; compData = static_cast(calloc(1, sizeof(Composite))); } - target->pImpl->compSource = source; compData->target = target; compData->source = source; compData->method = method; -- 2.7.4 From efe38299e4829cf6f202b417196f6d2dd1245ec1 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Sun, 15 Jan 2023 20:20:43 +0900 Subject: [PATCH 03/16] common shape: code refactoring & data optimization. re-design the shape data structure so that render backends are able to access them directly. This also let us remove tvgShape member data from the Shape::Impl. To achieve this, migrate shape/stroke/path from the canvas interface to the render interface. Change-Id: Iac398dedec39c6bc88fa4c1c3cfdb3f5db4c25e2 --- src/lib/gl_engine/tvgGlCommon.h | 2 +- src/lib/gl_engine/tvgGlGeometry.cpp | 15 +- src/lib/gl_engine/tvgGlGeometry.h | 6 +- src/lib/gl_engine/tvgGlRenderer.cpp | 27 ++- src/lib/gl_engine/tvgGlRenderer.h | 2 +- src/lib/sw_engine/tvgSwCommon.h | 10 +- src/lib/sw_engine/tvgSwRenderer.cpp | 38 ++-- src/lib/sw_engine/tvgSwRenderer.h | 2 +- src/lib/sw_engine/tvgSwShape.cpp | 42 ++--- src/lib/sw_engine/tvgSwStroke.cpp | 8 +- src/lib/tvgRender.h | 97 +++++++++- src/lib/tvgShape.cpp | 157 +++++++--------- src/lib/tvgShapeImpl.h | 351 +++++++++++++++--------------------- 13 files changed, 374 insertions(+), 383 deletions(-) diff --git a/src/lib/gl_engine/tvgGlCommon.h b/src/lib/gl_engine/tvgGlCommon.h index 90d6b14..0121900 100644 --- a/src/lib/gl_engine/tvgGlCommon.h +++ b/src/lib/gl_engine/tvgGlCommon.h @@ -54,7 +54,7 @@ class GlGeometry; struct GlShape { - const Shape* shape = nullptr; + const RenderShape* rshape = nullptr; float viewWd; float viewHt; RenderUpdateFlag updateFlag = None; diff --git a/src/lib/gl_engine/tvgGlGeometry.cpp b/src/lib/gl_engine/tvgGlGeometry.cpp index c3d3297..b8273e2 100644 --- a/src/lib/gl_engine/tvgGlGeometry.cpp +++ b/src/lib/gl_engine/tvgGlGeometry.cpp @@ -43,13 +43,12 @@ const GlSize GlGeometry::getPrimitiveSize(const uint32_t primitiveIndex) const } -bool GlGeometry::decomposeOutline(const Shape& shape) +bool GlGeometry::decomposeOutline(const RenderShape& rshape) { - const PathCommand* cmds = nullptr; - auto cmdCnt = shape.pathCommands(&cmds); - - Point* pts = nullptr; - auto ptsCnt = shape.pathCoords(const_cast(&pts)); + auto cmds = rshape.path.cmds; + auto cmdCnt = rshape.path.cmdCnt; + auto pts = rshape.path.pts; + auto ptsCnt = rshape.path.ptsCnt; //No actual shape data if (cmdCnt == 0 || ptsCnt == 0) return false; @@ -101,7 +100,7 @@ bool GlGeometry::decomposeOutline(const Shape& shape) return true; } -bool GlGeometry::generateAAPoints(TVG_UNUSED const Shape& shape, float strokeWd, RenderUpdateFlag flag) +bool GlGeometry::generateAAPoints(TVG_UNUSED const RenderShape& rshape, float strokeWd, RenderUpdateFlag flag) { for (auto& shapeGeometry : mPrimitives) { vector normalInfo; @@ -158,7 +157,7 @@ bool GlGeometry::generateAAPoints(TVG_UNUSED const Shape& shape, float strokeWd, return true; } -bool GlGeometry::tesselate(TVG_UNUSED const Shape& shape, float viewWd, float viewHt, RenderUpdateFlag flag) +bool GlGeometry::tesselate(TVG_UNUSED const RenderShape& rshape, float viewWd, float viewHt, RenderUpdateFlag flag) { for (auto& shapeGeometry : mPrimitives) { constexpr float opaque = 1.0f; diff --git a/src/lib/gl_engine/tvgGlGeometry.h b/src/lib/gl_engine/tvgGlGeometry.h index ec1b7e8..c5e5b29 100644 --- a/src/lib/gl_engine/tvgGlGeometry.h +++ b/src/lib/gl_engine/tvgGlGeometry.h @@ -237,9 +237,9 @@ public: uint32_t getPrimitiveCount(); const GlSize getPrimitiveSize(const uint32_t primitiveIndex) const; - bool decomposeOutline(const Shape& shape); - bool generateAAPoints(TVG_UNUSED const Shape& shape, float strokeWd, RenderUpdateFlag flag); - bool tesselate(TVG_UNUSED const Shape &shape, float viewWd, float viewHt, RenderUpdateFlag flag); + bool decomposeOutline(const RenderShape& rshape); + bool generateAAPoints(TVG_UNUSED const RenderShape& rshape, float strokeWd, RenderUpdateFlag flag); + bool tesselate(TVG_UNUSED const RenderShape& rshape, float viewWd, float viewHt, RenderUpdateFlag flag); void disableVertex(uint32_t location); void draw(const uint32_t location, const uint32_t primitiveIndex, RenderUpdateFlag flag); void updateTransform(const RenderTransform* transform, float w, float h); diff --git a/src/lib/gl_engine/tvgGlRenderer.cpp b/src/lib/gl_engine/tvgGlRenderer.cpp index 95582fb..3031f46 100644 --- a/src/lib/gl_engine/tvgGlRenderer.cpp +++ b/src/lib/gl_engine/tvgGlRenderer.cpp @@ -150,16 +150,13 @@ bool GlRenderer::renderShape(RenderData data) { if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform)) { - const Fill* gradient = sdata->shape->fill(); - if (gradient != nullptr) - { - drawPrimitive(*sdata, gradient, i, RenderUpdateFlag::Gradient); - } + auto gradient = sdata->rshape->fill; + if (gradient) drawPrimitive(*sdata, gradient, i, RenderUpdateFlag::Gradient); } if(flags & (RenderUpdateFlag::Color | RenderUpdateFlag::Transform)) { - sdata->shape->fillColor(&r, &g, &b, &a); + sdata->rshape->fillColor(&r, &g, &b, &a); if (a > 0) { drawPrimitive(*sdata, r, g, b, a, i, RenderUpdateFlag::Color); @@ -168,7 +165,7 @@ bool GlRenderer::renderShape(RenderData data) if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) { - sdata->shape->strokeColor(&r, &g, &b, &a); + sdata->rshape->strokeColor(&r, &g, &b, &a); if (a > 0) { drawPrimitive(*sdata, r, g, b, a, i, RenderUpdateFlag::Stroke); @@ -197,13 +194,13 @@ RenderData GlRenderer::prepare(TVG_UNUSED Surface* image, TVG_UNUSED RenderData } -RenderData GlRenderer::prepare(const Shape& shape, RenderData data, const RenderTransform* transform, TVG_UNUSED uint32_t opacity, Array& clips, RenderUpdateFlag flags, TVG_UNUSED bool clipper) +RenderData GlRenderer::prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, TVG_UNUSED uint32_t opacity, Array& clips, RenderUpdateFlag flags, TVG_UNUSED bool clipper) { //prepare shape data GlShape* sdata = static_cast(data); if (!sdata) { sdata = new GlShape; - sdata->shape = &shape; + sdata->rshape = &rshape; } sdata->viewWd = static_cast(surface.w); @@ -216,9 +213,9 @@ RenderData GlRenderer::prepare(const Shape& shape, RenderData data, const Render //invisible? uint8_t alphaF, alphaS; - shape.fillColor(nullptr, nullptr, nullptr, &alphaF); - shape.strokeColor(nullptr, nullptr, nullptr, &alphaS); - auto strokeWd = shape.strokeWidth(); + rshape.fillColor(nullptr, nullptr, nullptr, &alphaF); + rshape.strokeColor(nullptr, nullptr, nullptr, &alphaS); + auto strokeWd = rshape.strokeWidth(); if ( ((sdata->updateFlag & RenderUpdateFlag::Gradient) == 0) && ((sdata->updateFlag & RenderUpdateFlag::Color) && alphaF == 0) && @@ -231,9 +228,9 @@ RenderData GlRenderer::prepare(const Shape& shape, RenderData data, const Render if (sdata->updateFlag & (RenderUpdateFlag::Color | RenderUpdateFlag::Stroke | RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform) ) { - if (!sdata->geometry->decomposeOutline(shape)) return sdata; - if (!sdata->geometry->generateAAPoints(shape, static_cast(strokeWd), sdata->updateFlag)) return sdata; - if (!sdata->geometry->tesselate(shape, sdata->viewWd, sdata->viewHt, sdata->updateFlag)) return sdata; + if (!sdata->geometry->decomposeOutline(rshape)) return sdata; + if (!sdata->geometry->generateAAPoints(rshape, static_cast(strokeWd), sdata->updateFlag)) return sdata; + if (!sdata->geometry->tesselate(rshape, sdata->viewWd, sdata->viewHt, sdata->updateFlag)) return sdata; } return sdata; } diff --git a/src/lib/gl_engine/tvgGlRenderer.h b/src/lib/gl_engine/tvgGlRenderer.h index 2688566..4d1bb87 100644 --- a/src/lib/gl_engine/tvgGlRenderer.h +++ b/src/lib/gl_engine/tvgGlRenderer.h @@ -30,7 +30,7 @@ class GlRenderer : public RenderMethod public: Surface surface = {nullptr, 0, 0, 0}; - RenderData prepare(const Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags, bool clipper) override; + RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags, bool clipper) override; RenderData prepare(Surface* image, Polygon* triangles, uint32_t triangleCnt, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) override; bool preRender() override; bool renderShape(RenderData data) override; diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index 1f920eb..e177f61 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -302,12 +302,12 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, S 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); +bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite); bool shapePrepared(const SwShape* shape); -bool shapeGenRle(SwShape* shape, const Shape* sdata, bool antiAlias); +bool shapeGenRle(SwShape* shape, const RenderShape* rshape, bool antiAlias); void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid); -void shapeResetStroke(SwShape* shape, const Shape* sdata, const Matrix* transform); -bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid); +void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix* transform); +bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid); void shapeFree(SwShape* shape); void shapeDelStroke(SwShape* shape); bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable); @@ -317,7 +317,7 @@ void shapeResetStrokeFill(SwShape* shape); void shapeDelFill(SwShape* shape); void shapeDelStrokeFill(SwShape* shape); -void strokeReset(SwStroke* stroke, const Shape* shape, const Matrix* transform); +void strokeReset(SwStroke* stroke, const RenderShape* shape, const Matrix* transform); bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline); SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid); void strokeFree(SwStroke* stroke); diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index 93d0d5d..17dff57 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -72,7 +72,7 @@ struct SwTask : Task struct SwShapeTask : SwTask { SwShape shape; - const Shape* sdata = nullptr; + const RenderShape* rshape = nullptr; bool cmpStroking = false; bool clipper = false; @@ -85,9 +85,9 @@ struct SwShapeTask : SwTask bool visibleFill = false; auto clipRegion = bbox; - if (HALF_STROKE(sdata->strokeWidth()) > 0) { - sdata->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha); - visibleStroke = sdata->strokeFill() || (static_cast(strokeAlpha * opacity / 255) > 0); + if (HALF_STROKE(rshape->strokeWidth()) > 0) { + rshape->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha); + visibleStroke = rshape->strokeFill() || (static_cast(strokeAlpha * opacity / 255) > 0); } //This checks also for the case, if the invisible shape turned to visible by alpha. @@ -97,12 +97,12 @@ struct SwShapeTask : SwTask //Shape if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform) || prepareShape) { uint8_t alpha = 0; - sdata->fillColor(nullptr, nullptr, nullptr, &alpha); + rshape->fillColor(nullptr, nullptr, nullptr, &alpha); alpha = static_cast(static_cast(alpha) * opacity / 255); - visibleFill = (alpha > 0 || sdata->fill()); + visibleFill = (alpha > 0 || rshape->fill); if (visibleFill || visibleStroke || clipper) { shapeReset(&shape); - if (!shapePrepare(&shape, sdata, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) goto err; + if (!shapePrepare(&shape, rshape, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) goto err; } } @@ -117,11 +117,11 @@ struct SwShapeTask : SwTask shape outline below stroke could be full covered by stroke drawing. Thus it turns off antialising in that condition. Also, it shouldn't be dash style. */ - auto antiAlias = (strokeAlpha == 255 && sdata->strokeWidth() > 2 && sdata->strokeDash(nullptr) == 0) ? false : true; + auto antiAlias = (strokeAlpha == 255 && rshape->strokeWidth() > 2 && rshape->strokeDash(nullptr) == 0) ? false : true; - if (!shapeGenRle(&shape, sdata, antiAlias)) goto err; + if (!shapeGenRle(&shape, rshape, antiAlias)) goto err; } - if (auto fill = sdata->fill()) { + if (auto fill = rshape->fill) { auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false; if (ctable) shapeResetFill(&shape); if (!shapeGenFillColors(&shape, fill, transform, surface, cmpStroking ? 255 : opacity, ctable)) goto err; @@ -133,10 +133,10 @@ struct SwShapeTask : SwTask //Stroke if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) { if (visibleStroke) { - shapeResetStroke(&shape, sdata, transform); - if (!shapeGenStrokeRle(&shape, sdata, transform, clipRegion, bbox, mpool, tid)) goto err; + shapeResetStroke(&shape, rshape, transform); + if (!shapeGenStrokeRle(&shape, rshape, transform, clipRegion, bbox, mpool, tid)) goto err; - if (auto fill = sdata->strokeFill()) { + if (auto fill = rshape->strokeFill()) { auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false; if (ctable) shapeResetStrokeFill(&shape); if (!shapeGenStrokeFillColors(&shape, fill, transform, surface, cmpStroking ? 255 : opacity, ctable)) goto err; @@ -397,18 +397,18 @@ bool SwRenderer::renderShape(RenderData data) //Main raster stage uint8_t r, g, b, a; - if (auto fill = task->sdata->fill()) { + if (auto fill = task->rshape->fill) { rasterGradientShape(surface, &task->shape, fill->identifier()); } else { - task->sdata->fillColor(&r, &g, &b, &a); + task->rshape->fillColor(&r, &g, &b, &a); a = static_cast((opacity * (uint32_t) a) / 255); if (a > 0) rasterShape(surface, &task->shape, r, g, b, a); } - if (auto strokeFill = task->sdata->strokeFill()) { + if (auto strokeFill = task->rshape->strokeFill()) { rasterGradientStroke(surface, &task->shape, strokeFill->identifier()); } else { - if (task->sdata->strokeColor(&r, &g, &b, &a) == Result::Success) { + if (task->rshape->strokeColor(&r, &g, &b, &a)) { a = static_cast((opacity * (uint32_t) a) / 255); if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a); } @@ -643,13 +643,13 @@ RenderData SwRenderer::prepare(Surface* image, Polygon* triangles, uint32_t tria } -RenderData SwRenderer::prepare(const Shape& sdata, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags, bool clipper) +RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags, bool clipper) { //prepare task auto task = static_cast(data); if (!task) { task = new SwShapeTask; - task->sdata = &sdata; + task->rshape = &rshape; task->clipper = clipper; } return prepareCommon(task, transform, opacity, clips, flags); diff --git a/src/lib/sw_engine/tvgSwRenderer.h b/src/lib/sw_engine/tvgSwRenderer.h index 819141f..c3eadbd 100644 --- a/src/lib/sw_engine/tvgSwRenderer.h +++ b/src/lib/sw_engine/tvgSwRenderer.h @@ -36,7 +36,7 @@ namespace tvg class SwRenderer : public RenderMethod { public: - RenderData prepare(const Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags, bool clipper) override; + RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags, bool clipper) override; RenderData prepare(Surface* image, Polygon* triangles, uint32_t triangleCnt, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) override; bool preRender() override; bool renderShape(RenderData data) override; diff --git a/src/lib/sw_engine/tvgSwShape.cpp b/src/lib/sw_engine/tvgSwShape.cpp index 82d812e..7462a7b 100644 --- a/src/lib/sw_engine/tvgSwShape.cpp +++ b/src/lib/sw_engine/tvgSwShape.cpp @@ -267,13 +267,13 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct } -static SwOutline* _genDashOutline(const Shape* sdata, const Matrix* transform) +static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform) { - const PathCommand* cmds = nullptr; - auto cmdCnt = sdata->pathCommands(&cmds); + const PathCommand* cmds = rshape->path.cmds; + auto cmdCnt = rshape->path.cmdCnt; - const Point* pts = nullptr; - auto ptsCnt = sdata->pathCoords(&pts); + const Point* pts = rshape->path.pts; + auto ptsCnt = rshape->path.ptsCnt; //No actual shape data if (cmdCnt == 0 || ptsCnt == 0) return nullptr; @@ -286,7 +286,7 @@ static SwOutline* _genDashOutline(const Shape* sdata, const Matrix* transform) dash.curOpGap = false; const float* pattern; - dash.cnt = sdata->strokeDash(&pattern); + dash.cnt = rshape->strokeDash(&pattern); if (dash.cnt == 0) return nullptr; //OPTMIZE ME: Use mempool??? @@ -381,13 +381,13 @@ static bool _axisAlignedRect(const SwOutline* outline) -static bool _genOutline(SwShape* shape, const Shape* sdata, const Matrix* transform, SwMpool* mpool, unsigned tid, bool hasComposite) +static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix* transform, SwMpool* mpool, unsigned tid, bool hasComposite) { - const PathCommand* cmds = nullptr; - auto cmdCnt = sdata->pathCommands(&cmds); + const PathCommand* cmds = rshape->path.cmds; + auto cmdCnt = rshape->path.cmdCnt; - const Point* pts = nullptr; - auto ptsCnt = sdata->pathCoords(&pts); + const Point* pts = rshape->path.pts; + auto ptsCnt = rshape->path.ptsCnt; //No actual shape data if (cmdCnt == 0 || ptsCnt == 0) return false; @@ -467,7 +467,7 @@ static bool _genOutline(SwShape* shape, const Shape* sdata, const Matrix* transf _outlineEnd(*outline); - outline->fillRule = sdata->fillRule(); + outline->fillRule = rshape->rule; shape->outline = outline; shape->fastTrack = (!hasComposite && _axisAlignedRect(shape->outline)); @@ -479,9 +479,9 @@ static bool _genOutline(SwShape* shape, const Shape* sdata, const Matrix* transf /* External Class Implementation */ /************************************************************************/ -bool shapePrepare(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite) +bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite) { - if (!_genOutline(shape, sdata, transform, mpool, tid, hasComposite)) return false; + if (!_genOutline(shape, rshape, transform, mpool, tid, hasComposite)) return false; if (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion, shape->fastTrack)) return false; //Keep it for Rasterization Region @@ -504,7 +504,7 @@ bool shapePrepared(const SwShape* shape) } -bool shapeGenRle(SwShape* shape, TVG_UNUSED const Shape* sdata, bool antiAlias) +bool shapeGenRle(SwShape* shape, TVG_UNUSED const RenderShape* rshape, bool antiAlias) { //FIXME: Should we draw it? //Case: Stroke Line @@ -558,18 +558,18 @@ void shapeDelStroke(SwShape* shape) } -void shapeResetStroke(SwShape* shape, const Shape* sdata, const Matrix* transform) +void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix* transform) { if (!shape->stroke) shape->stroke = static_cast(calloc(1, sizeof(SwStroke))); auto stroke = shape->stroke; if (!stroke) return; - strokeReset(stroke, sdata, transform); + strokeReset(stroke, rshape, transform); rleReset(shape->strokeRle); } -bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid) +bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid) { SwOutline* shapeOutline = nullptr; SwOutline* strokeOutline = nullptr; @@ -577,14 +577,14 @@ bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transfo bool ret = true; //Dash Style Stroke - if (sdata->strokeDash(nullptr) > 0) { - shapeOutline = _genDashOutline(sdata, transform); + if (rshape->strokeDash(nullptr) > 0) { + shapeOutline = _genDashOutline(rshape, transform); if (!shapeOutline) return false; freeOutline = true; //Normal Style stroke } else { if (!shape->outline) { - if (!_genOutline(shape, sdata, transform, mpool, tid, false)) return false; + if (!_genOutline(shape, rshape, transform, mpool, tid, false)) return false; } shapeOutline = shape->outline; } diff --git a/src/lib/sw_engine/tvgSwStroke.cpp b/src/lib/sw_engine/tvgSwStroke.cpp index d1803bd..c404bf9 100644 --- a/src/lib/sw_engine/tvgSwStroke.cpp +++ b/src/lib/sw_engine/tvgSwStroke.cpp @@ -826,7 +826,7 @@ void strokeFree(SwStroke* stroke) } -void strokeReset(SwStroke* stroke, const Shape* sdata, const Matrix* transform) +void strokeReset(SwStroke* stroke, const RenderShape* rshape, const Matrix* transform) { if (transform) { stroke->sx = sqrtf(powf(transform->e11, 2.0f) + powf(transform->e21, 2.0f)); @@ -835,11 +835,11 @@ void strokeReset(SwStroke* stroke, const Shape* sdata, const Matrix* transform) stroke->sx = stroke->sy = 1.0f; } - stroke->width = HALF_STROKE(sdata->strokeWidth()); - stroke->cap = sdata->strokeCap(); + stroke->width = HALF_STROKE(rshape->strokeWidth()); + stroke->cap = rshape->strokeCap(); //Save line join: it can be temporarily changed when stroking curves... - stroke->joinSaved = stroke->join = sdata->strokeJoin(); + stroke->joinSaved = stroke->join = rshape->strokeJoin(); stroke->borders[0].ptsCnt = 0; stroke->borders[0].start = -1; diff --git a/src/lib/tvgRender.h b/src/lib/tvgRender.h index 88fe502..e9ad821 100644 --- a/src/lib/tvgRender.h +++ b/src/lib/tvgRender.h @@ -85,12 +85,107 @@ struct RenderTransform RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs); }; +struct RenderStroke +{ + float width = 0.0f; + uint8_t color[4] = {0, 0, 0, 0}; + Fill *fill = nullptr; + float* dashPattern = nullptr; + uint32_t dashCnt = 0; + StrokeCap cap = StrokeCap::Square; + StrokeJoin join = StrokeJoin::Bevel; + + ~RenderStroke() + { + free(dashPattern); + if (fill) delete(fill); + } +}; + +struct RenderShape +{ + struct + { + PathCommand* cmds = nullptr; + uint32_t cmdCnt = 0; + uint32_t reservedCmdCnt = 0; + + Point *pts = nullptr; + uint32_t ptsCnt = 0; + uint32_t reservedPtsCnt = 0; + } path; + + Fill *fill = nullptr; + RenderStroke *stroke = nullptr; + uint8_t color[4] = {0, 0, 0, 0}; //r, g, b, a + FillRule rule = FillRule::Winding; + + ~RenderShape() + { + free(path.cmds); + free(path.pts); + + if (fill) delete(fill); + if (stroke) delete(stroke); + } + + void fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const + { + if (r) *r = color[0]; + if (g) *g = color[1]; + if (b) *b = color[2]; + if (a) *a = color[3]; + } + + float strokeWidth() const + { + if (!stroke) return 0; + return stroke->width; + } + + bool strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const + { + if (!stroke) return false; + + if (r) *r = stroke->color[0]; + if (g) *g = stroke->color[1]; + if (b) *b = stroke->color[2]; + if (a) *a = stroke->color[3]; + + return true; + } + + const Fill* strokeFill() const + { + if (!stroke) return nullptr; + return stroke->fill; + } + + uint32_t strokeDash(const float** dashPattern) const + { + if (!stroke) return 0; + if (dashPattern) *dashPattern = stroke->dashPattern; + return stroke->dashCnt; + } + + StrokeCap strokeCap() const + { + if (!stroke) return StrokeCap::Square; + return stroke->cap; + } + + StrokeJoin strokeJoin() const + { + if (!stroke) return StrokeJoin::Bevel; + return stroke->join; + } +}; class RenderMethod { public: virtual ~RenderMethod() {} - virtual RenderData prepare(const Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags, bool clipper) = 0; + virtual RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags, bool clipper) = 0; virtual RenderData prepare(Surface* image, Polygon* triangles, uint32_t triangleCnt, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) = 0; virtual bool preRender() = 0; virtual bool renderShape(RenderData data) = 0; diff --git a/src/lib/tvgShape.cpp b/src/lib/tvgShape.cpp index 23291eb..3d12698 100644 --- a/src/lib/tvgShape.cpp +++ b/src/lib/tvgShape.cpp @@ -32,7 +32,7 @@ constexpr auto PATH_KAPPA = 0.552284f; /* External Class Implementation */ /************************************************************************/ -Shape :: Shape() : pImpl(new Impl(this)) +Shape :: Shape() : pImpl(new Impl()) { Paint::pImpl->id = TVG_CLASS_ID_SHAPE; Paint::pImpl->method(new PaintMethod(pImpl)); @@ -59,8 +59,7 @@ uint32_t Shape::identifier() noexcept Result Shape::reset() noexcept { - pImpl->path.reset(); - pImpl->flag = RenderUpdateFlag::Path; + pImpl->reset(); return Result::Success; } @@ -70,9 +69,9 @@ uint32_t Shape::pathCommands(const PathCommand** cmds) const noexcept { if (!cmds) return 0; - *cmds = pImpl->path.cmds; + *cmds = pImpl->rs.path.cmds; - return pImpl->path.cmdCnt; + return pImpl->rs.path.cmdCnt; } @@ -80,9 +79,9 @@ uint32_t Shape::pathCoords(const Point** pts) const noexcept { if (!pts) return 0; - *pts = pImpl->path.pts; + *pts = pImpl->rs.path.pts; - return pImpl->path.ptsCnt; + return pImpl->rs.path.ptsCnt; } @@ -90,10 +89,8 @@ Result Shape::appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point* { if (cmdCnt == 0 || ptsCnt == 0 || !cmds || !pts) return Result::InvalidArguments; - pImpl->path.grow(cmdCnt, ptsCnt); - pImpl->path.append(cmds, cmdCnt, pts, ptsCnt); - - pImpl->flag |= RenderUpdateFlag::Path; + pImpl->grow(cmdCnt, ptsCnt); + pImpl->append(cmds, cmdCnt, pts, ptsCnt); return Result::Success; } @@ -101,9 +98,7 @@ Result Shape::appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point* Result Shape::moveTo(float x, float y) noexcept { - pImpl->path.moveTo(x, y); - - pImpl->flag |= RenderUpdateFlag::Path; + pImpl->moveTo(x, y); return Result::Success; } @@ -111,9 +106,7 @@ Result Shape::moveTo(float x, float y) noexcept Result Shape::lineTo(float x, float y) noexcept { - pImpl->path.lineTo(x, y); - - pImpl->flag |= RenderUpdateFlag::Path; + pImpl->lineTo(x, y); return Result::Success; } @@ -121,9 +114,7 @@ Result Shape::lineTo(float x, float y) noexcept Result Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) noexcept { - pImpl->path.cubicTo(cx1, cy1, cx2, cy2, x, y); - - pImpl->flag |= RenderUpdateFlag::Path; + pImpl->cubicTo(cx1, cy1, cx2, cy2, x, y); return Result::Success; } @@ -131,9 +122,7 @@ Result Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float Result Shape::close() noexcept { - pImpl->path.close(); - - pImpl->flag |= RenderUpdateFlag::Path; + pImpl->close(); return Result::Success; } @@ -144,15 +133,13 @@ Result Shape::appendCircle(float cx, float cy, float rx, float ry) noexcept auto rxKappa = rx * PATH_KAPPA; auto ryKappa = ry * PATH_KAPPA; - pImpl->path.grow(6, 13); - pImpl->path.moveTo(cx, cy - ry); - pImpl->path.cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy); - pImpl->path.cubicTo(cx + rx, cy + ryKappa, cx + rxKappa, cy + ry, cx, cy + ry); - pImpl->path.cubicTo(cx - rxKappa, cy + ry, cx - rx, cy + ryKappa, cx - rx, cy); - pImpl->path.cubicTo(cx - rx, cy - ryKappa, cx - rxKappa, cy - ry, cx, cy - ry); - pImpl->path.close(); - - pImpl->flag |= RenderUpdateFlag::Path; + pImpl->grow(6, 13); + pImpl->moveTo(cx, cy - ry); + pImpl->cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy); + pImpl->cubicTo(cx + rx, cy + ryKappa, cx + rxKappa, cy + ry, cx, cy + ry); + pImpl->cubicTo(cx - rxKappa, cy + ry, cx - rx, cy + ryKappa, cx - rx, cy); + pImpl->cubicTo(cx - rx, cy - ryKappa, cx - rxKappa, cy - ry, cx, cy - ry); + pImpl->close(); return Result::Success; } @@ -174,10 +161,10 @@ Result Shape::appendArc(float cx, float cy, float radius, float startAngle, floa Point start = {radius * cosf(startAngle), radius * sinf(startAngle)}; if (pie) { - pImpl->path.moveTo(cx, cy); - pImpl->path.lineTo(start.x + cx, start.y + cy); + pImpl->moveTo(cx, cy); + pImpl->lineTo(start.x + cx, start.y + cy); } else { - pImpl->path.moveTo(start.x + cx, start.y + cy); + pImpl->moveTo(start.x + cx, start.y + cy); } for (int i = 0; i < nCurves; ++i) { @@ -204,14 +191,12 @@ Result Shape::appendArc(float cx, float cy, float radius, float startAngle, floa Point ctrl1 = {ax - k2 * ay + cx, ay + k2 * ax + cy}; Point ctrl2 = {bx + k2 * by + cx, by - k2 * bx + cy}; - pImpl->path.cubicTo(ctrl1.x, ctrl1.y, ctrl2.x, ctrl2.y, end.x, end.y); + pImpl->cubicTo(ctrl1.x, ctrl1.y, ctrl2.x, ctrl2.y, end.x, end.y); startAngle = endAngle; } - if (pie) pImpl->path.close(); - - pImpl->flag |= RenderUpdateFlag::Path; + if (pie) pImpl->close(); return Result::Success; } @@ -228,48 +213,46 @@ Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry) //rectangle if (rx == 0 && ry == 0) { - pImpl->path.grow(5, 4); - pImpl->path.moveTo(x, y); - pImpl->path.lineTo(x + w, y); - pImpl->path.lineTo(x + w, y + h); - pImpl->path.lineTo(x, y + h); - pImpl->path.close(); + pImpl->grow(5, 4); + pImpl->moveTo(x, y); + pImpl->lineTo(x + w, y); + pImpl->lineTo(x + w, y + h); + pImpl->lineTo(x, y + h); + pImpl->close(); //circle } 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; auto hry = ry * 0.5f; - pImpl->path.grow(10, 17); - pImpl->path.moveTo(x + rx, y); - pImpl->path.lineTo(x + w - rx, y); - pImpl->path.cubicTo(x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry); - pImpl->path.lineTo(x + w, y + h - ry); - pImpl->path.cubicTo(x + w, y + h - ry + hry, x + w - rx + hrx, y + h, x + w - rx, y + h); - pImpl->path.lineTo(x + rx, y + h); - pImpl->path.cubicTo(x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry); - pImpl->path.lineTo(x, y + ry); - pImpl->path.cubicTo(x, y + ry - hry, x + rx - hrx, y, x + rx, y); - pImpl->path.close(); + pImpl->grow(10, 17); + pImpl->moveTo(x + rx, y); + pImpl->lineTo(x + w - rx, y); + pImpl->cubicTo(x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry); + pImpl->lineTo(x + w, y + h - ry); + pImpl->cubicTo(x + w, y + h - ry + hry, x + w - rx + hrx, y + h, x + w - rx, y + h); + pImpl->lineTo(x + rx, y + h); + pImpl->cubicTo(x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry); + pImpl->lineTo(x, y + ry); + pImpl->cubicTo(x, y + ry - hry, x + rx - hrx, y, x + rx, y); + pImpl->close(); } - pImpl->flag |= RenderUpdateFlag::Path; - return Result::Success; } Result Shape::fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept { - pImpl->color[0] = r; - pImpl->color[1] = g; - pImpl->color[2] = b; - pImpl->color[3] = a; + pImpl->rs.color[0] = r; + pImpl->rs.color[1] = g; + pImpl->rs.color[2] = b; + pImpl->rs.color[3] = a; pImpl->flag |= RenderUpdateFlag::Color; - if (pImpl->fill) { - delete(pImpl->fill); - pImpl->fill = nullptr; + if (pImpl->rs.fill) { + delete(pImpl->rs.fill); + pImpl->rs.fill = nullptr; pImpl->flag |= RenderUpdateFlag::Gradient; } @@ -282,8 +265,8 @@ Result Shape::fill(unique_ptr f) noexcept auto p = f.release(); if (!p) return Result::MemoryCorruption; - if (pImpl->fill && pImpl->fill != p) delete(pImpl->fill); - pImpl->fill = p; + if (pImpl->rs.fill && pImpl->rs.fill != p) delete(pImpl->rs.fill); + pImpl->rs.fill = p; pImpl->flag |= RenderUpdateFlag::Gradient; return Result::Success; @@ -292,17 +275,15 @@ Result Shape::fill(unique_ptr f) noexcept Result Shape::fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept { - if (r) *r = pImpl->color[0]; - if (g) *g = pImpl->color[1]; - if (b) *b = pImpl->color[2]; - if (a) *a = pImpl->color[3]; + pImpl->rs.fillColor(r, g, b, a); return Result::Success; } + const Fill* Shape::fill() const noexcept { - return pImpl->fill; + return pImpl->rs.fill; } @@ -316,8 +297,7 @@ Result Shape::stroke(float width) noexcept float Shape::strokeWidth() const noexcept { - if (!pImpl->stroke) return 0; - return pImpl->stroke->width; + return pImpl->rs.strokeWidth(); } @@ -331,12 +311,7 @@ Result Shape::stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept Result Shape::strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept { - if (!pImpl->stroke) return Result::InsufficientCondition; - - if (r) *r = pImpl->stroke->color[0]; - if (g) *g = pImpl->stroke->color[1]; - if (b) *b = pImpl->stroke->color[2]; - if (a) *a = pImpl->stroke->color[3]; + if (!pImpl->rs.strokeColor(r, g, b, a)) return Result::InsufficientCondition; return Result::Success; } @@ -350,9 +325,7 @@ Result Shape::stroke(unique_ptr f) noexcept const Fill* Shape::strokeFill() const noexcept { - if (!pImpl->stroke) return nullptr; - - return pImpl->stroke->fill; + return pImpl->rs.strokeFill(); } @@ -373,11 +346,7 @@ Result Shape::stroke(const float* dashPattern, uint32_t cnt) noexcept uint32_t Shape::strokeDash(const float** dashPattern) const noexcept { - if (!pImpl->stroke) return 0; - - if (dashPattern) *dashPattern = pImpl->stroke->dashPattern; - - return pImpl->stroke->dashCnt; + return pImpl->rs.strokeDash(dashPattern); } @@ -399,23 +368,19 @@ Result Shape::stroke(StrokeJoin join) noexcept StrokeCap Shape::strokeCap() const noexcept { - if (!pImpl->stroke) return StrokeCap::Square; - - return pImpl->stroke->cap; + return pImpl->rs.strokeCap(); } StrokeJoin Shape::strokeJoin() const noexcept { - if (!pImpl->stroke) return StrokeJoin::Bevel; - - return pImpl->stroke->join; + return pImpl->rs.strokeJoin(); } Result Shape::fill(FillRule r) noexcept { - pImpl->rule = r; + pImpl->rs.rule = r; return Result::Success; } @@ -423,5 +388,5 @@ Result Shape::fill(FillRule r) noexcept FillRule Shape::fillRule() const noexcept { - return pImpl->rule; + return pImpl->rs.rule; } diff --git a/src/lib/tvgShapeImpl.h b/src/lib/tvgShapeImpl.h index fa2786f..9f39dfa 100644 --- a/src/lib/tvgShapeImpl.h +++ b/src/lib/tvgShapeImpl.h @@ -30,243 +30,155 @@ /* Internal Class Implementation */ /************************************************************************/ -struct ShapeStroke +struct Shape::Impl { - float width; - uint8_t color[4]; - Fill *fill; - float* dashPattern; - uint32_t dashCnt; - StrokeCap cap; - StrokeJoin join; - - void copy(const ShapeStroke* src) + RenderShape rs; //shape data + RenderData rdata = nullptr; //engine data + uint32_t flag = RenderUpdateFlag::None; + + bool dispose(RenderMethod& renderer) { - width = src->width; - dashCnt = src->dashCnt; - cap = src->cap; - join = src->join; - - memcpy(color, src->color, sizeof(color)); - if (dashCnt > 0) { - dashPattern = static_cast(malloc(sizeof(float) * dashCnt)); - memcpy(dashPattern, src->dashPattern, sizeof(float) * dashCnt); - } - if (src->fill) fill = src->fill->duplicate(); + auto ret = renderer.dispose(rdata); + rdata = nullptr; + return ret; } - void clear() + bool render(RenderMethod& renderer) { - if (dashPattern) free(dashPattern); - if (fill) delete(fill); + return renderer.renderShape(rdata); } -}; - - -struct ShapePath -{ - PathCommand* cmds = nullptr; - uint32_t cmdCnt = 0; - uint32_t reservedCmdCnt = 0; - - Point *pts = nullptr; - uint32_t ptsCnt = 0; - uint32_t reservedPtsCnt = 0; - ~ShapePath() + void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag pFlag, bool clipper) { - if (cmds) free(cmds); - if (pts) free(pts); + rdata = renderer.prepare(rs, rdata, transform, opacity, clips, static_cast(pFlag | flag), clipper); + flag = RenderUpdateFlag::None; + return rdata; } - ShapePath() + RenderRegion bounds(RenderMethod& renderer) { + return renderer.region(rdata); } - void duplicate(const ShapePath* src) + bool bounds(float* x, float* y, float* w, float* h) { - if (src->cmdCnt == 0 || src->ptsCnt == 0) return; - - cmdCnt = src->cmdCnt; - reservedCmdCnt = src->reservedCmdCnt; - ptsCnt = src->ptsCnt; - reservedPtsCnt = src->reservedPtsCnt; + //Path bounding size + if (rs.path.ptsCnt > 0 ) { + Point min = { rs.path.pts[0].x, rs.path.pts[0].y }; + Point max = { rs.path.pts[0].x, rs.path.pts[0].y }; + + for (uint32_t i = 1; i < rs.path.ptsCnt; ++i) { + if (rs.path.pts[i].x < min.x) min.x = rs.path.pts[i].x; + if (rs.path.pts[i].y < min.y) min.y = rs.path.pts[i].y; + if (rs.path.pts[i].x > max.x) max.x = rs.path.pts[i].x; + if (rs.path.pts[i].y > max.y) max.y = rs.path.pts[i].y; + } - cmds = static_cast(malloc(sizeof(PathCommand) * reservedCmdCnt)); - if (!cmds) return; - memcpy(cmds, src->cmds, sizeof(PathCommand) * cmdCnt); + if (x) *x = min.x; + if (y) *y = min.y; + if (w) *w = max.x - min.x; + if (h) *h = max.y - min.y; + } - pts = static_cast(malloc(sizeof(Point) * reservedPtsCnt)); - if (!pts) { - free(cmds); - return; + //Stroke feathering + if (rs.stroke) { + if (x) *x -= rs.stroke->width * 0.5f; + if (y) *y -= rs.stroke->width * 0.5f; + if (w) *w += rs.stroke->width; + if (h) *h += rs.stroke->width; } - memcpy(pts, src->pts, sizeof(Point) * ptsCnt); + return rs.path.ptsCnt > 0 ? true : false; } void reserveCmd(uint32_t cmdCnt) { - if (cmdCnt <= reservedCmdCnt) return; - reservedCmdCnt = cmdCnt; - cmds = static_cast(realloc(cmds, sizeof(PathCommand) * reservedCmdCnt)); + if (cmdCnt <= rs.path.reservedCmdCnt) return; + rs.path.reservedCmdCnt = cmdCnt; + rs.path.cmds = static_cast(realloc(rs.path.cmds, sizeof(PathCommand) * rs.path.reservedCmdCnt)); } void reservePts(uint32_t ptsCnt) { - if (ptsCnt <= reservedPtsCnt) return; - reservedPtsCnt = ptsCnt; - pts = static_cast(realloc(pts, sizeof(Point) * reservedPtsCnt)); + if (ptsCnt <= rs.path.reservedPtsCnt) return; + rs.path.reservedPtsCnt = ptsCnt; + rs.path.pts = static_cast(realloc(rs.path.pts, sizeof(Point) * rs.path.reservedPtsCnt)); } void grow(uint32_t cmdCnt, uint32_t ptsCnt) { - reserveCmd(this->cmdCnt + cmdCnt); - reservePts(this->ptsCnt + ptsCnt); + reserveCmd(rs.path.cmdCnt + cmdCnt); + reservePts(rs.path.ptsCnt + ptsCnt); } void reset() { - cmdCnt = 0; - ptsCnt = 0; - } - - void append(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt) - { - memcpy(this->cmds + this->cmdCnt, cmds, sizeof(PathCommand) * cmdCnt); - memcpy(this->pts + this->ptsCnt, pts, sizeof(Point) * ptsCnt); - this->cmdCnt += cmdCnt; - this->ptsCnt += ptsCnt; - } - - void moveTo(float x, float y) - { - if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2); - if (ptsCnt + 2 > reservedPtsCnt) reservePts((ptsCnt + 2) * 2); + rs.path.cmdCnt = 0; + rs.path.ptsCnt = 0; - cmds[cmdCnt++] = PathCommand::MoveTo; - pts[ptsCnt++] = {x, y}; + flag = RenderUpdateFlag::Path; } - void lineTo(float x, float y) + void append(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt) { - if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2); - if (ptsCnt + 2 > reservedPtsCnt) reservePts((ptsCnt + 2) * 2); + memcpy(rs.path.cmds + rs.path.cmdCnt, cmds, sizeof(PathCommand) * cmdCnt); + memcpy(rs.path.pts + rs.path.ptsCnt, pts, sizeof(Point) * ptsCnt); + rs.path.cmdCnt += cmdCnt; + rs.path.ptsCnt += ptsCnt; - cmds[cmdCnt++] = PathCommand::LineTo; - pts[ptsCnt++] = {x, y}; + flag |= RenderUpdateFlag::Path; } - void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) + void moveTo(float x, float y) { - if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2); - if (ptsCnt + 3 > reservedPtsCnt) reservePts((ptsCnt + 3) * 2); + if (rs.path.cmdCnt + 1 > rs.path.reservedCmdCnt) reserveCmd((rs.path.cmdCnt + 1) * 2); + if (rs.path.ptsCnt + 2 > rs.path.reservedPtsCnt) reservePts((rs.path.ptsCnt + 2) * 2); - cmds[cmdCnt++] = PathCommand::CubicTo; - pts[ptsCnt++] = {cx1, cy1}; - pts[ptsCnt++] = {cx2, cy2}; - pts[ptsCnt++] = {x, y}; - } + rs.path.cmds[rs.path.cmdCnt++] = PathCommand::MoveTo; + rs.path.pts[rs.path.ptsCnt++] = {x, y}; - void close() - { - if (cmdCnt > 0 && cmds[cmdCnt - 1] == PathCommand::Close) return; - - if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2); - cmds[cmdCnt++] = PathCommand::Close; + flag |= RenderUpdateFlag::Path; } - bool bounds(float* x, float* y, float* w, float* h) const + void lineTo(float x, float y) { - if (ptsCnt == 0) return false; - - Point min = { pts[0].x, pts[0].y }; - Point max = { pts[0].x, pts[0].y }; - - for (uint32_t i = 1; i < ptsCnt; ++i) { - if (pts[i].x < min.x) min.x = pts[i].x; - if (pts[i].y < min.y) min.y = pts[i].y; - if (pts[i].x > max.x) max.x = pts[i].x; - if (pts[i].y > max.y) max.y = pts[i].y; - } - - if (x) *x = min.x; - if (y) *y = min.y; - if (w) *w = max.x - min.x; - if (h) *h = max.y - min.y; - - return true; - } -}; - - -struct Shape::Impl -{ - ShapePath path; - Fill *fill = nullptr; - ShapeStroke *stroke = nullptr; - uint8_t color[4] = {0, 0, 0, 0}; //r, g, b, a - FillRule rule = FillRule::Winding; - RenderData rdata = nullptr; //engine data - Shape *shape = nullptr; - uint32_t flag = RenderUpdateFlag::None; + if (rs.path.cmdCnt + 1 > rs.path.reservedCmdCnt) reserveCmd((rs.path.cmdCnt + 1) * 2); + if (rs.path.ptsCnt + 2 > rs.path.reservedPtsCnt) reservePts((rs.path.ptsCnt + 2) * 2); - Impl(Shape* s) : shape(s) - { - } + rs.path.cmds[rs.path.cmdCnt++] = PathCommand::LineTo; + rs.path.pts[rs.path.ptsCnt++] = {x, y}; - ~Impl() - { - if (fill) delete(fill); - if (stroke) { - stroke->clear(); - free (stroke); - } + flag |= RenderUpdateFlag::Path; } - bool dispose(RenderMethod& renderer) + void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) { - auto ret = renderer.dispose(rdata); - rdata = nullptr; - return ret; - } + if (rs.path.cmdCnt + 1 > rs.path.reservedCmdCnt) reserveCmd((rs.path.cmdCnt + 1) * 2); + if (rs.path.ptsCnt + 3 > rs.path.reservedPtsCnt) reservePts((rs.path.ptsCnt + 3) * 2); - bool render(RenderMethod& renderer) - { - return renderer.renderShape(rdata); - } + rs.path.cmds[rs.path.cmdCnt++] = PathCommand::CubicTo; + rs.path.pts[rs.path.ptsCnt++] = {cx1, cy1}; + rs.path.pts[rs.path.ptsCnt++] = {cx2, cy2}; + rs.path.pts[rs.path.ptsCnt++] = {x, y}; - void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag pFlag, bool clipper) - { - this->rdata = renderer.prepare(*shape, this->rdata, transform, opacity, clips, static_cast(pFlag | flag), clipper); - flag = RenderUpdateFlag::None; - return this->rdata; + flag |= RenderUpdateFlag::Path; } - RenderRegion bounds(RenderMethod& renderer) + void close() { - return renderer.region(rdata); - } + if (rs.path.cmdCnt > 0 && rs.path.cmds[rs.path.cmdCnt - 1] == PathCommand::Close) return; - bool bounds(float* x, float* y, float* w, float* h) - { - auto ret = path.bounds(x, y, w, h); + if (rs.path.cmdCnt + 1 > rs.path.reservedCmdCnt) reserveCmd((rs.path.cmdCnt + 1) * 2); + rs.path.cmds[rs.path.cmdCnt++] = PathCommand::Close; - //Stroke feathering - if (stroke) { - if (x) *x -= stroke->width * 0.5f; - if (y) *y -= stroke->width * 0.5f; - if (w) *w += stroke->width; - if (h) *h += stroke->width; - } - return ret; + flag |= RenderUpdateFlag::Path; } bool strokeWidth(float width) { //TODO: Size Exception? - if (!stroke) stroke = static_cast(calloc(sizeof(ShapeStroke), 1)); - stroke->width = width; + if (!rs.stroke) rs.stroke = new RenderStroke(); + rs.stroke->width = width; flag |= RenderUpdateFlag::Stroke; return true; @@ -274,8 +186,8 @@ struct Shape::Impl bool strokeCap(StrokeCap cap) { - if (!stroke) stroke = static_cast(calloc(sizeof(ShapeStroke), 1)); - stroke->cap = cap; + if (!rs.stroke) rs.stroke = new RenderStroke(); + rs.stroke->cap = cap; flag |= RenderUpdateFlag::Stroke; return true; @@ -283,8 +195,8 @@ struct Shape::Impl bool strokeJoin(StrokeJoin join) { - if (!stroke) stroke = static_cast(calloc(sizeof(ShapeStroke), 1)); - stroke->join = join; + if (!rs.stroke) rs.stroke = new RenderStroke(); + rs.stroke->join = join; flag |= RenderUpdateFlag::Stroke; return true; @@ -292,17 +204,17 @@ struct Shape::Impl bool strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { - if (!stroke) stroke = static_cast(calloc(sizeof(ShapeStroke), 1)); - if (stroke->fill) { - delete(stroke->fill); - stroke->fill = nullptr; + if (!rs.stroke) rs.stroke = new RenderStroke(); + if (rs.stroke->fill) { + delete(rs.stroke->fill); + rs.stroke->fill = nullptr; flag |= RenderUpdateFlag::GradientStroke; } - stroke->color[0] = r; - stroke->color[1] = g; - stroke->color[2] = b; - stroke->color[3] = a; + rs.stroke->color[0] = r; + rs.stroke->color[1] = g; + rs.stroke->color[2] = b; + rs.stroke->color[3] = a; flag |= RenderUpdateFlag::Stroke; @@ -314,9 +226,9 @@ struct Shape::Impl auto p = f.release(); if (!p) return Result::MemoryCorruption; - if (!stroke) stroke = static_cast(calloc(sizeof(ShapeStroke), 1)); - if (stroke->fill && stroke->fill != p) delete(stroke->fill); - stroke->fill = p; + if (!rs.stroke) rs.stroke = new RenderStroke(); + if (rs.stroke->fill && rs.stroke->fill != p) delete(rs.stroke->fill); + rs.stroke->fill = p; flag |= RenderUpdateFlag::Stroke; flag |= RenderUpdateFlag::GradientStroke; @@ -328,23 +240,23 @@ struct Shape::Impl { //Reset dash if (!pattern && cnt == 0) { - free(stroke->dashPattern); - stroke->dashPattern = nullptr; + free(rs.stroke->dashPattern); + rs.stroke->dashPattern = nullptr; } else { - if (!stroke) stroke = static_cast(calloc(sizeof(ShapeStroke), 1)); - if (stroke->dashCnt != cnt) { - free(stroke->dashPattern); - stroke->dashPattern = nullptr; + if (!rs.stroke) rs.stroke = new RenderStroke(); + if (rs.stroke->dashCnt != cnt) { + free(rs.stroke->dashPattern); + rs.stroke->dashPattern = nullptr; } - if (!stroke->dashPattern) { - stroke->dashPattern = static_cast(malloc(sizeof(float) * cnt)); - if (!stroke->dashPattern) return false; + if (!rs.stroke->dashPattern) { + rs.stroke->dashPattern = static_cast(malloc(sizeof(float) * cnt)); + if (!rs.stroke->dashPattern) return false; } for (uint32_t i = 0; i < cnt; ++i) { - stroke->dashPattern[i] = pattern[i]; + rs.stroke->dashPattern[i] = pattern[i]; } } - stroke->dashCnt = cnt; + rs.stroke->dashCnt = cnt; flag |= RenderUpdateFlag::Stroke; return true; @@ -355,29 +267,52 @@ struct Shape::Impl auto ret = Shape::gen(); auto dup = ret.get()->pImpl; - dup->rule = rule; + dup->rs.rule = rs.rule; //Color - memcpy(dup->color, color, sizeof(color)); + memcpy(dup->rs.color, rs.color, sizeof(rs.color)); dup->flag = RenderUpdateFlag::Color; //Path - dup->path.duplicate(&path); + if (rs.path.cmdCnt > 0 && rs.path.ptsCnt > 0) { + dup->rs.path.cmdCnt = rs.path.cmdCnt; + dup->rs.path.reservedCmdCnt = rs.path.reservedCmdCnt; + dup->rs.path.ptsCnt = rs.path.ptsCnt; + dup->rs.path.reservedPtsCnt = rs.path.reservedPtsCnt; + + dup->rs.path.cmds = static_cast(malloc(sizeof(PathCommand) * dup->rs.path.reservedCmdCnt)); + if (dup->rs.path.cmds) memcpy(dup->rs.path.cmds, rs.path.cmds, sizeof(PathCommand) * dup->rs.path.cmdCnt); + + dup->rs.path.pts = static_cast(malloc(sizeof(Point) * dup->rs.path.reservedPtsCnt)); + if (dup->rs.path.pts) memcpy(dup->rs.path.pts, rs.path.pts, sizeof(Point) * dup->rs.path.ptsCnt); + } dup->flag |= RenderUpdateFlag::Path; //Stroke - if (stroke) { - dup->stroke = static_cast(calloc(sizeof(ShapeStroke), 1)); - dup->stroke->copy(stroke); + if (rs.stroke) { + dup->rs.stroke = new RenderStroke(); + dup->rs.stroke->width = rs.stroke->width; + dup->rs.stroke->dashCnt = rs.stroke->dashCnt; + dup->rs.stroke->cap = rs.stroke->cap; + dup->rs.stroke->join = rs.stroke->join; + memcpy(dup->rs.stroke->color, rs.stroke->color, sizeof(rs.stroke->color)); + + if (rs.stroke->dashCnt > 0) { + dup->rs.stroke->dashPattern = static_cast(malloc(sizeof(float) * rs.stroke->dashCnt)); + memcpy(dup->rs.stroke->dashPattern, rs.stroke->dashPattern, sizeof(float) * rs.stroke->dashCnt); + } + dup->flag |= RenderUpdateFlag::Stroke; - if (stroke->fill) + if (rs.stroke->fill) { + dup->rs.stroke->fill = rs.stroke->fill->duplicate(); dup->flag |= RenderUpdateFlag::GradientStroke; + } } //Fill - if (fill) { - dup->fill = fill->duplicate(); + if (rs.fill) { + dup->rs.fill = rs.fill->duplicate(); dup->flag |= RenderUpdateFlag::Gradient; } -- 2.7.4 From 76231d664046b9b078355c7831211be191abc425 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Sun, 5 Feb 2023 11:42:37 +0900 Subject: [PATCH 04/16] gl_engine: -- compiler warnings MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit ../src/lib/gl_engine/tvgGlRenderer.cpp: In member function ‘virtual bool GlRenderer::renderShape(tvg::RenderData)’: ../src/lib/gl_engine/tvgGlRenderer.cpp:171:30: warning: ‘a’ may be used uninitialized in this function [-Wmaybe-uninitialized] 171 | drawPrimitive(*sdata, r, g, b, a, i, RenderUpdateFlag::Stroke); | ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ../src/lib/gl_engine/tvgGlRenderer.cpp:171:30: warning: ‘b’ may be used uninitialized in this function [-Wmaybe-uninitialized] ../src/lib/gl_engine/tvgGlRenderer.cpp:171:30: warning: ‘g’ may be used uninitialized in this function [-Wmaybe-uninitialized] ../src/lib/gl_engine/tvgGlRenderer.cpp:171:30: warning: ‘r’ may be used uninitialized in this function [-Wmaybe-uninitialized] ../src/lib/gl_engine/tvgGlRenderer.cpp: In member function ‘virtual void* GlRenderer::prepare(const tvg::RenderShape&, tvg::RenderData, const tvg::RenderTransform*, uint32_t, tvg::Array&, tvg::RenderUpdateFlag, bool)’: ../src/lib/gl_engine/tvgGlRenderer.cpp:215:21: warning: ‘alphaS’ may be used uninitialized in this function [-Wmaybe-uninitialized] 215 | uint8_t alphaF, alphaS; Change-Id: I65d6cc0b6449342232287f295c06ef640e50b389 --- src/lib/gl_engine/tvgGlRenderer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/gl_engine/tvgGlRenderer.cpp b/src/lib/gl_engine/tvgGlRenderer.cpp index 3031f46..517e64e 100644 --- a/src/lib/gl_engine/tvgGlRenderer.cpp +++ b/src/lib/gl_engine/tvgGlRenderer.cpp @@ -140,7 +140,7 @@ bool GlRenderer::renderShape(RenderData data) auto sdata = static_cast(data); if (!sdata) return false; - uint8_t r, g, b, a; + uint8_t r = 0, g = 0, b = 0, a = 0; size_t flags = static_cast(sdata->updateFlag); GL_CHECK(glViewport(0, 0, (GLsizei)sdata->viewWd, (GLsizei)sdata->viewHt)); @@ -212,7 +212,7 @@ RenderData GlRenderer::prepare(const RenderShape& rshape, RenderData data, const sdata->geometry = make_unique(); //invisible? - uint8_t alphaF, alphaS; + uint8_t alphaF = 0, alphaS = 0; rshape.fillColor(nullptr, nullptr, nullptr, &alphaF); rshape.strokeColor(nullptr, nullptr, nullptr, &alphaS); auto strokeWd = rshape.strokeWidth(); -- 2.7.4 From 3311bccc0d92ea1536b654a2e5cfa1c8f4c5d14c Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Sun, 5 Feb 2023 11:44:59 +0900 Subject: [PATCH 05/16] common shape: code refactoring. keep name consistency. Change-Id: I902d163f50457e7e82180ad9259e8952257cd302 --- src/lib/tvgShapeImpl.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib/tvgShapeImpl.h b/src/lib/tvgShapeImpl.h index 9f39dfa..74988c3 100644 --- a/src/lib/tvgShapeImpl.h +++ b/src/lib/tvgShapeImpl.h @@ -33,31 +33,31 @@ struct Shape::Impl { RenderShape rs; //shape data - RenderData rdata = nullptr; //engine data + RenderData rd = nullptr; //engine data uint32_t flag = RenderUpdateFlag::None; bool dispose(RenderMethod& renderer) { - auto ret = renderer.dispose(rdata); - rdata = nullptr; + auto ret = renderer.dispose(rd); + rd = nullptr; return ret; } bool render(RenderMethod& renderer) { - return renderer.renderShape(rdata); + return renderer.renderShape(rd); } void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag pFlag, bool clipper) { - rdata = renderer.prepare(rs, rdata, transform, opacity, clips, static_cast(pFlag | flag), clipper); + rd = renderer.prepare(rs, rd, transform, opacity, clips, static_cast(pFlag | flag), clipper); flag = RenderUpdateFlag::None; - return rdata; + return rd; } RenderRegion bounds(RenderMethod& renderer) { - return renderer.region(rdata); + return renderer.region(rd); } bool bounds(float* x, float* y, float* w, float* h) -- 2.7.4 From b2dc9433f70278c29b659081627399ecd3dfd6b9 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Fri, 20 Jan 2023 13:13:01 +0900 Subject: [PATCH 06/16] loader: Support ABGR colorspace Since the color space is set at the time of specifying the target buffer of the canvas, there is no way to know the color space when the picture is loaded. So, check the color space applied to SwCanvas at the time of reload() and change the color space. There is an issue of BGR color space support for each loader. The external_jpg loader resets the TJPF color space and calls read() to get a new buffer. In the case of external_png, we need to change the color value directly because it have to start over from begin_read_*. This solution can affect performance as much as it access again image buffer that have already been `read()` done. However, this only happens once. Change-Id: Ib7f25b5bc5871cfe59ad3963d26536269b48a3f5 --- src/lib/gl_engine/tvgGlRenderer.cpp | 7 +++++++ src/lib/gl_engine/tvgGlRenderer.h | 2 ++ src/lib/sw_engine/tvgSwRenderer.cpp | 11 ++++++++-- src/lib/sw_engine/tvgSwRenderer.h | 4 +++- src/lib/tvgLoadModule.h | 3 ++- src/lib/tvgPictureImpl.h | 4 +++- src/lib/tvgRender.h | 2 ++ src/loaders/external_jpg/tvgJpgLoader.cpp | 30 ++++++++++++++++++++++---- src/loaders/external_jpg/tvgJpgLoader.h | 2 +- src/loaders/external_png/tvgPngLoader.cpp | 29 ++++++++++++++++++++++--- src/loaders/external_png/tvgPngLoader.h | 4 ++-- src/loaders/jpg/tvgJpgLoader.cpp | 28 ++++++++++++++++++++++--- src/loaders/jpg/tvgJpgLoader.h | 2 +- src/loaders/png/tvgPngLoader.cpp | 28 ++++++++++++++++++++++--- src/loaders/png/tvgPngLoader.h | 2 +- src/loaders/raw/tvgRawLoader.cpp | 35 ++++++++++++++++++++++++------- src/loaders/raw/tvgRawLoader.h | 4 ++-- 17 files changed, 165 insertions(+), 32 deletions(-) diff --git a/src/lib/gl_engine/tvgGlRenderer.cpp b/src/lib/gl_engine/tvgGlRenderer.cpp index 517e64e..fcd33ad 100644 --- a/src/lib/gl_engine/tvgGlRenderer.cpp +++ b/src/lib/gl_engine/tvgGlRenderer.cpp @@ -123,6 +123,13 @@ bool GlRenderer::endComposite(TVG_UNUSED Compositor* cmp) } +int32_t GlRenderer::colorSpace() +{ + //TODO: return a proper color space value. + return -1; +} + + bool GlRenderer::renderImage(TVG_UNUSED void* data) { return false; diff --git a/src/lib/gl_engine/tvgGlRenderer.h b/src/lib/gl_engine/tvgGlRenderer.h index 4d1bb87..3263231 100644 --- a/src/lib/gl_engine/tvgGlRenderer.h +++ b/src/lib/gl_engine/tvgGlRenderer.h @@ -50,6 +50,8 @@ public: bool beginComposite(Compositor* cmp, CompositeMethod method, uint32_t opacity) override; bool endComposite(Compositor* cmp) override; + uint32_t colorSpace() override; + static GlRenderer* gen(); static int init(TVG_UNUSED uint32_t threads); static int32_t init(); diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index 17dff57..6a1a881 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -296,7 +296,7 @@ bool SwRenderer::viewport(const RenderRegion& vp) } -bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t cs) +bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t colorSpace) { if (!buffer || stride == 0 || w == 0 || h == 0 || w > stride) return false; @@ -306,7 +306,7 @@ bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t surface->stride = stride; surface->w = w; surface->h = h; - surface->cs = cs; + surface->cs = colorSpace; vport.x = vport.y = 0; vport.w = surface->w; @@ -661,6 +661,13 @@ SwRenderer::SwRenderer():mpool(globalMpool) } +uint32_t SwRenderer::colorSpace() +{ + if (surface) return surface->cs; + return tvg::SwCanvas::ARGB8888; +} + + bool SwRenderer::init(uint32_t threads) { if ((initEngineCnt++) > 0) return true; diff --git a/src/lib/sw_engine/tvgSwRenderer.h b/src/lib/sw_engine/tvgSwRenderer.h index c3eadbd..690b7ff 100644 --- a/src/lib/sw_engine/tvgSwRenderer.h +++ b/src/lib/sw_engine/tvgSwRenderer.h @@ -50,7 +50,7 @@ public: bool clear() override; bool sync() override; - bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t cs); + bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t colorSpace); bool mempool(bool shared); Compositor* target(const RenderRegion& region) override; @@ -58,6 +58,8 @@ public: bool endComposite(Compositor* cmp) override; void clearCompositors(); + uint32_t colorSpace() override; + static SwRenderer* gen(); static bool init(uint32_t threads); static int32_t init(); diff --git a/src/lib/tvgLoadModule.h b/src/lib/tvgLoadModule.h index 2c2ffda..4637c90 100644 --- a/src/lib/tvgLoadModule.h +++ b/src/lib/tvgLoadModule.h @@ -37,6 +37,7 @@ public: float vw = 0; float vh = 0; float w = 0, h = 0; //default image size + uint32_t colorSpace = SwCanvas::ARGB8888; virtual ~LoadModule() {} @@ -49,7 +50,7 @@ public: virtual bool read() = 0; virtual bool close() = 0; - virtual unique_ptr bitmap() { return nullptr; } + virtual unique_ptr bitmap(uint32_t colorSpace) { return nullptr; } virtual unique_ptr paint() { return nullptr; } }; diff --git a/src/lib/tvgPictureImpl.h b/src/lib/tvgPictureImpl.h index b7e2ba0..6ef2313 100644 --- a/src/lib/tvgPictureImpl.h +++ b/src/lib/tvgPictureImpl.h @@ -69,6 +69,7 @@ struct Picture::Impl void* rdata = nullptr; //engine data float w = 0, h = 0; bool resizing = false; + uint32_t rendererColorSpace = 0; ~Impl() { @@ -104,7 +105,7 @@ struct Picture::Impl } } free(surface); - if ((surface = loader->bitmap().release())) { + if ((surface = loader->bitmap(rendererColorSpace).release())) { loader->close(); return RenderUpdateFlag::Image; } @@ -128,6 +129,7 @@ struct Picture::Impl void* update(RenderMethod &renderer, const RenderTransform* pTransform, uint32_t opacity, Array& clips, RenderUpdateFlag pFlag, bool clipper) { + rendererColorSpace = renderer.colorSpace(); auto flag = reload(); if (surface) { diff --git a/src/lib/tvgRender.h b/src/lib/tvgRender.h index e9ad821..55bbec1 100644 --- a/src/lib/tvgRender.h +++ b/src/lib/tvgRender.h @@ -203,6 +203,8 @@ public: virtual Compositor* target(const RenderRegion& region) = 0; virtual bool beginComposite(Compositor* cmp, CompositeMethod method, uint32_t opacity) = 0; virtual bool endComposite(Compositor* cmp) = 0; + + virtual uint32_t colorSpace() = 0; }; } diff --git a/src/loaders/external_jpg/tvgJpgLoader.cpp b/src/loaders/external_jpg/tvgJpgLoader.cpp index 5920f7c..7ead54c 100644 --- a/src/loaders/external_jpg/tvgJpgLoader.cpp +++ b/src/loaders/external_jpg/tvgJpgLoader.cpp @@ -37,6 +37,24 @@ void JpgLoader::clear() freeData = false; } +uint32_t convertColorSpaceType(uint32_t colorSpace) +{ + uint32_t tjpfColorSpace = TJPF_RGBX; + switch (colorSpace) + { + case SwCanvas::ARGB8888: + case SwCanvas::ARGB8888_STRAIGHT: + default: + tjpfColorSpace = TJPF_BGRX; + break; + case SwCanvas::ABGR8888: + case SwCanvas::ABGR8888_STRAIGHT: + tjpfColorSpace = TJPF_RGBX; + break; + } + return tjpfColorSpace; +} + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ @@ -127,11 +145,11 @@ bool JpgLoader::open(const char* data, uint32_t size, bool copy) bool JpgLoader::read() { if (image) tjFree(image); - image = (unsigned char *)tjAlloc(static_cast(w) * static_cast(h) * tjPixelSize[TJPF_BGRX]); + image = (unsigned char *)tjAlloc(static_cast(w) * static_cast(h) * tjPixelSize[convertColorSpaceType(colorSpace)]); if (!image) return false; //decompress jpg image - if (tjDecompress2(jpegDecompressor, data, size, image, static_cast(w), 0, static_cast(h), TJPF_BGRX, 0) < 0) { + if (tjDecompress2(jpegDecompressor, data, size, image, static_cast(w), 0, static_cast(h), convertColorSpaceType(colorSpace), 0) < 0) { TVGERR("JPG LOADER", "%s", tjGetErrorStr()); tjFree(image); image = nullptr; @@ -149,16 +167,20 @@ bool JpgLoader::close() } -unique_ptr JpgLoader::bitmap() +unique_ptr JpgLoader::bitmap(uint32_t colorSpace) { if (!image) return nullptr; + if (this->colorSpace != colorSpace) { + this->colorSpace = colorSpace; + read(); + } auto surface = static_cast(malloc(sizeof(Surface))); surface->buffer = (uint32_t*)(image); surface->stride = w; surface->w = w; surface->h = h; - surface->cs = SwCanvas::ARGB8888; + surface->cs = colorSpace; return unique_ptr(surface); } diff --git a/src/loaders/external_jpg/tvgJpgLoader.h b/src/loaders/external_jpg/tvgJpgLoader.h index c8c9345..da7b59e 100644 --- a/src/loaders/external_jpg/tvgJpgLoader.h +++ b/src/loaders/external_jpg/tvgJpgLoader.h @@ -38,7 +38,7 @@ public: bool read() override; bool close() override; - unique_ptr bitmap() override; + unique_ptr bitmap(uint32_t colorSpace) override; private: void clear(); diff --git a/src/loaders/external_png/tvgPngLoader.cpp b/src/loaders/external_png/tvgPngLoader.cpp index 4061a49..2dbedd8 100644 --- a/src/loaders/external_png/tvgPngLoader.cpp +++ b/src/loaders/external_png/tvgPngLoader.cpp @@ -42,6 +42,24 @@ static void _premultiply(uint32_t* data, uint32_t w, uint32_t h) } +static inline uint32_t CHANGE_COLORSPACE(uint32_t c) +{ + return (c & 0xff000000) + ((c & 0x00ff0000)>>16) + (c & 0x0000ff00) + ((c & 0x000000ff)<<16); +} + + +static void _changeColorSpace(uint32_t* data, uint32_t w, uint32_t h) +{ + auto buffer = data; + for (uint32_t y = 0; y < h; ++y, buffer += w) { + auto src = buffer; + for (uint32_t x = 0; x < w; ++x, ++src) { + *src = CHANGE_COLORSPACE(*src); + } + } +} + + PngLoader::PngLoader() { image = static_cast(calloc(1, sizeof(png_image))); @@ -110,16 +128,21 @@ bool PngLoader::close() return true; } -unique_ptr PngLoader::bitmap() +unique_ptr PngLoader::bitmap(uint32_t colorSpace) { if (!content) return nullptr; + if (this->colorSpace != colorSpace) { + this->colorSpace = colorSpace; + _changeColorSpace(content, w, h); + } auto surface = static_cast(malloc(sizeof(Surface))); - surface->buffer = (uint32_t*)(content); + surface->buffer = content; surface->stride = w; surface->w = w; surface->h = h; - surface->cs = SwCanvas::ARGB8888; + surface->cs = colorSpace; return unique_ptr(surface); } + diff --git a/src/loaders/external_png/tvgPngLoader.h b/src/loaders/external_png/tvgPngLoader.h index 39d5fdb..48f44f5 100644 --- a/src/loaders/external_png/tvgPngLoader.h +++ b/src/loaders/external_png/tvgPngLoader.h @@ -37,11 +37,11 @@ public: bool read() override; bool close() override; - unique_ptr bitmap() override; + unique_ptr bitmap(uint32_t colorSpace) override; private: png_imagep image = nullptr; - const uint32_t* content = nullptr; + uint32_t* content = nullptr; }; #endif //_TVG_PNG_LOADER_H_ diff --git a/src/loaders/jpg/tvgJpgLoader.cpp b/src/loaders/jpg/tvgJpgLoader.cpp index dc50848..2b16090 100644 --- a/src/loaders/jpg/tvgJpgLoader.cpp +++ b/src/loaders/jpg/tvgJpgLoader.cpp @@ -28,6 +28,24 @@ /* Internal Class Implementation */ /************************************************************************/ +static inline uint32_t CHANGE_COLORSPACE(uint32_t c) +{ + return (c & 0xff000000) + ((c & 0x00ff0000)>>16) + (c & 0x0000ff00) + ((c & 0x000000ff)<<16); +} + + +static void _changeColorSpace(uint32_t* data, uint32_t w, uint32_t h) +{ + auto buffer = data; + for (uint32_t y = 0; y < h; ++y, buffer += w) { + auto src = buffer; + for (uint32_t x = 0; x < w; ++x, ++src) { + *src = CHANGE_COLORSPACE(*src); + } + } +} + + void JpgLoader::clear() { jpgdDelete(decoder); @@ -110,18 +128,22 @@ bool JpgLoader::close() } -unique_ptr JpgLoader::bitmap() +unique_ptr JpgLoader::bitmap(uint32_t colorSpace) { this->done(); if (!image) return nullptr; + if (this->colorSpace != colorSpace) { + this->colorSpace = colorSpace; + _changeColorSpace(reinterpret_cast(image), w, h); + } auto surface = static_cast(malloc(sizeof(Surface))); - surface->buffer = (uint32_t*)(image); + surface->buffer = reinterpret_cast(image); surface->stride = static_cast(w); surface->w = static_cast(w); surface->h = static_cast(h); - surface->cs = SwCanvas::ARGB8888; + surface->cs = colorSpace; return unique_ptr(surface); } diff --git a/src/loaders/jpg/tvgJpgLoader.h b/src/loaders/jpg/tvgJpgLoader.h index 6d2febe..02aee45 100644 --- a/src/loaders/jpg/tvgJpgLoader.h +++ b/src/loaders/jpg/tvgJpgLoader.h @@ -45,7 +45,7 @@ public: bool read() override; bool close() override; - unique_ptr bitmap() override; + unique_ptr bitmap(uint32_t colorSpace) override; void run(unsigned tid) override; }; diff --git a/src/loaders/png/tvgPngLoader.cpp b/src/loaders/png/tvgPngLoader.cpp index cc44b0d..7233f30 100644 --- a/src/loaders/png/tvgPngLoader.cpp +++ b/src/loaders/png/tvgPngLoader.cpp @@ -49,6 +49,24 @@ static void _premultiply(uint32_t* data, uint32_t w, uint32_t h) } +static inline uint32_t CHANGE_COLORSPACE(uint32_t c) +{ + return (c & 0xff000000) + ((c & 0x00ff0000)>>16) + (c & 0x0000ff00) + ((c & 0x000000ff)<<16); +} + + +static void _changeColorSpace(uint32_t* data, uint32_t w, uint32_t h) +{ + auto buffer = data; + for (uint32_t y = 0; y < h; ++y, buffer += w) { + auto src = buffer; + for (uint32_t x = 0; x < w; ++x, ++src) { + *src = CHANGE_COLORSPACE(*src); + } + } +} + + void PngLoader::clear() { lodepng_state_cleanup(&state); @@ -163,18 +181,22 @@ bool PngLoader::close() } -unique_ptr PngLoader::bitmap() +unique_ptr PngLoader::bitmap(uint32_t colorSpace) { this->done(); if (!image) return nullptr; + if (this->colorSpace != colorSpace) { + this->colorSpace = colorSpace; + _changeColorSpace(reinterpret_cast(image), w, h); + } auto surface = static_cast(malloc(sizeof(Surface))); - surface->buffer = (uint32_t*)(image); + surface->buffer = reinterpret_cast(image); surface->stride = static_cast(w); surface->w = static_cast(w); surface->h = static_cast(h); - surface->cs = SwCanvas::ARGB8888; + surface->cs = colorSpace; return unique_ptr(surface); } diff --git a/src/loaders/png/tvgPngLoader.h b/src/loaders/png/tvgPngLoader.h index 579d197..5e3c930 100644 --- a/src/loaders/png/tvgPngLoader.h +++ b/src/loaders/png/tvgPngLoader.h @@ -48,7 +48,7 @@ public: bool read() override; bool close() override; - unique_ptr bitmap() override; + unique_ptr bitmap(uint32_t colorSpace) override; void run(unsigned tid) override; }; diff --git a/src/loaders/raw/tvgRawLoader.cpp b/src/loaders/raw/tvgRawLoader.cpp index d8c0559..524cff1 100644 --- a/src/loaders/raw/tvgRawLoader.cpp +++ b/src/loaders/raw/tvgRawLoader.cpp @@ -29,6 +29,23 @@ /* Internal Class Implementation */ /************************************************************************/ +static inline uint32_t CHANGE_COLORSPACE(uint32_t c) +{ + return (c & 0xff000000) + ((c & 0x00ff0000)>>16) + (c & 0x0000ff00) + ((c & 0x000000ff)<<16); +} + + +static void _changeColorSpace(uint32_t* data, uint32_t w, uint32_t h) +{ + auto buffer = data; + for (uint32_t y = 0; y < h; ++y, buffer += w) { + auto src = buffer; + for (uint32_t x = 0; x < w; ++x, ++src) { + *src = CHANGE_COLORSPACE(*src); + } + } +} + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ @@ -55,7 +72,7 @@ bool RawLoader::open(const uint32_t* data, uint32_t w, uint32_t h, bool copy) if (!content) return false; memcpy((void*)content, data, sizeof(uint32_t) * w * h); } - else content = data; + else content = const_cast(data); return true; } @@ -73,16 +90,20 @@ bool RawLoader::close() } -unique_ptr RawLoader::bitmap() +unique_ptr RawLoader::bitmap(uint32_t colorSpace) { if (!content) return nullptr; + if (this->colorSpace != colorSpace) { + this->colorSpace = colorSpace; + _changeColorSpace(content, w, h); + } auto surface = static_cast(malloc(sizeof(Surface))); - surface->buffer = (uint32_t*)(content); - surface->stride = (uint32_t)w; - surface->w = (uint32_t)w; - surface->h = (uint32_t)h; - surface->cs = SwCanvas::ARGB8888; + surface->buffer = content; + surface->stride = static_cast(w); + surface->w = static_cast(w); + surface->h = static_cast(h); + surface->cs = colorSpace; return unique_ptr(surface); } diff --git a/src/loaders/raw/tvgRawLoader.h b/src/loaders/raw/tvgRawLoader.h index d381010..a5e3d59 100644 --- a/src/loaders/raw/tvgRawLoader.h +++ b/src/loaders/raw/tvgRawLoader.h @@ -26,7 +26,7 @@ class RawLoader : public LoadModule { public: - const uint32_t* content = nullptr; + uint32_t* content = nullptr; bool copy = false; ~RawLoader(); @@ -36,7 +36,7 @@ public: bool read() override; bool close() override; - unique_ptr bitmap() override; + unique_ptr bitmap(uint32_t colorSpace) override; }; -- 2.7.4 From 776a4f4d2fab0a40c7ed672ca8bfc2bb3dd5ff48 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Fri, 10 Feb 2023 10:50:16 +0900 Subject: [PATCH 07/16] png_loader(static): Fix the colorspace of an image with an alpha channel Set colorspace to ABGR when colortype of lodepng is LCT_RGBA. Since an image without an alpha channel becomes an ARGB colorspace with LCT_RGB, it is the same as the default colorspace. Change-Id: Ia33ca50ab3340fe72f1b7af2ced35850854fc835 --- src/loaders/png/tvgPngLoader.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/loaders/png/tvgPngLoader.cpp b/src/loaders/png/tvgPngLoader.cpp index 7233f30..d415332 100644 --- a/src/loaders/png/tvgPngLoader.cpp +++ b/src/loaders/png/tvgPngLoader.cpp @@ -213,5 +213,7 @@ void PngLoader::run(unsigned tid) lodepng_decode(&image, &width, &height, &state, data, size); + if (state.info_png.color.colortype == LCT_RGBA) colorSpace = SwCanvas::ABGR8888; + _premultiply((uint32_t*)(image), width, height); } -- 2.7.4 From af593fe0eccc5fd337bbc12e2358db9a8541b02f Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Fri, 10 Feb 2023 13:46:06 +0900 Subject: [PATCH 08/16] png_loader(static): Move the colortype check code to open() Change-Id: I6711b294b24e3e89bf93f96e0c78f8026ac75375 --- src/loaders/png/tvgPngLoader.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/loaders/png/tvgPngLoader.cpp b/src/loaders/png/tvgPngLoader.cpp index d415332..2d8f78b 100644 --- a/src/loaders/png/tvgPngLoader.cpp +++ b/src/loaders/png/tvgPngLoader.cpp @@ -125,6 +125,8 @@ bool PngLoader::open(const string& path) h = static_cast(height); ret = true; + if (state.info_png.color.colortype == LCT_RGBA) colorSpace = SwCanvas::ABGR8888; + goto finalize; failure: @@ -159,6 +161,8 @@ bool PngLoader::open(const char* data, uint32_t size, bool copy) h = static_cast(height); this->size = size; + if (state.info_png.color.colortype == LCT_RGBA) colorSpace = SwCanvas::ABGR8888; + return true; } @@ -213,7 +217,5 @@ void PngLoader::run(unsigned tid) lodepng_decode(&image, &width, &height, &state, data, size); - if (state.info_png.color.colortype == LCT_RGBA) colorSpace = SwCanvas::ABGR8888; - _premultiply((uint32_t*)(image), width, height); } -- 2.7.4 From 48719759e834522753e224cff8c0bb517fbc3cde Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Tue, 14 Mar 2023 23:39:48 +0900 Subject: [PATCH 09/16] sw_engine - ++safety Prevent any potential crash. @Issues: https://github.com/thorvg/thorvg/issues/1327 Change-Id: I57e6582f17a3beae475d85261439aa78ca5f6919 --- src/lib/sw_engine/tvgSwRasterTexmap.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/sw_engine/tvgSwRasterTexmap.h b/src/lib/sw_engine/tvgSwRasterTexmap.h index 58906b6..40d4037 100644 --- a/src/lib/sw_engine/tvgSwRasterTexmap.h +++ b/src/lib/sw_engine/tvgSwRasterTexmap.h @@ -548,7 +548,7 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans) static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox* region, uint32_t opacity, uint32_t (*blendMethod)(uint32_t)) { //Exceptions: No dedicated drawing area? - if (!region && image->rle->size == 0) return false; + if (!image->rle || (!region && image->rle->size == 0)) return false; /* Prepare vertices. shift XY coordinates to match the sub-pixeling technique. */ @@ -605,7 +605,7 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, const Polygon* triangles, const uint32_t triangleCount, const Matrix* transform, const SwBBox* region, uint32_t opacity, uint32_t (*blendMethod)(uint32_t)) { //Exceptions: No dedicated drawing area? - if (!region && image->rle->size == 0) return false; + if (!image->rle || (!region && image->rle->size == 0)) return false; // Step polygons once to transform auto transformedTris = (Polygon*)malloc(sizeof(Polygon) * triangleCount); -- 2.7.4 From 456f8c62ad81d8ce9fc6e4c3366f6719019a3bb6 Mon Sep 17 00:00:00 2001 From: jykeon Date: Thu, 16 Mar 2023 10:16:57 +0900 Subject: [PATCH 10/16] Bump up 0.8.9 Change-Id: I9c28a1e14fa3cb2bcad516b119f82316b730b130 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 f39d1fd..9f6f5a0 100644 --- a/packaging/thorvg.spec +++ b/packaging/thorvg.spec @@ -1,6 +1,6 @@ Name: thorvg Summary: Thor Vector Graphics Library -Version: 0.8.8 +Version: 0.8.9 Release: 1 Group: Graphics System/Rendering Engine License: MIT -- 2.7.4 From 47aa64fd7a1bee9526773f26e5e1d33d12c6fc02 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Wed, 1 Feb 2023 23:46:48 +0100 Subject: [PATCH 11/16] svg_loader: handling svgs without viewBox/viewPort Additionally: - cases of inforrect viewBox values are handled - cases of zero width/height of a viewBox and/or viewPort @Issue: https://github.com/Samsung/thorvg/issues/1239 Change-Id: I7f6b557ae451db27d1a2033eb2976ed87e0e7f7d Signed-off-by: jykeon --- src/lib/tvgPicture.cpp | 1 + src/lib/tvgPictureImpl.h | 7 ++- src/loaders/svg/tvgSvgLoader.cpp | 85 ++++++++++++++++++++++------------ src/loaders/svg/tvgSvgLoader.h | 1 + src/loaders/svg/tvgSvgLoaderCommon.h | 9 ++++ src/loaders/svg/tvgSvgSceneBuilder.cpp | 19 ++++++-- src/loaders/svg/tvgSvgSceneBuilder.h | 2 +- 7 files changed, 90 insertions(+), 34 deletions(-) diff --git a/src/lib/tvgPicture.cpp b/src/lib/tvgPicture.cpp index 2b1d0f5..0af5430 100644 --- a/src/lib/tvgPicture.cpp +++ b/src/lib/tvgPicture.cpp @@ -98,6 +98,7 @@ Result Picture::size(float w, float h) noexcept Result Picture::size(float* w, float* h) const noexcept { if (!pImpl->loader) return Result::InsufficientCondition; + pImpl->reload(); if (w) *w = pImpl->w; if (h) *h = pImpl->h; return Result::Success; diff --git a/src/lib/tvgPictureImpl.h b/src/lib/tvgPictureImpl.h index 6ef2313..4c9b2a1 100644 --- a/src/lib/tvgPictureImpl.h +++ b/src/lib/tvgPictureImpl.h @@ -98,6 +98,10 @@ struct Picture::Impl paint = p.release(); loader->close(); if (w != loader->w || h != loader->h) { + if (!resizing) { + w = loader->w; + h = loader->h; + } loader->resize(paint, w, h); resizing = false; } @@ -155,9 +159,10 @@ struct Picture::Impl return false; } - bool viewbox(float* x, float* y, float* w, float* h) const + bool viewbox(float* x, float* y, float* w, float* h) { if (!loader) return false; + reload(); if (x) *x = loader->vx; if (y) *y = loader->vy; if (w) *w = loader->vw; diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 073f0cf..e05aa49 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -831,20 +831,32 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value) if (!strcmp(key, "width")) { doc->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal); + doc->viewFlag = (SvgViewFlag)((uint32_t)doc->viewFlag | (uint32_t)SvgViewFlag::Width); } else if (!strcmp(key, "height")) { doc->h = _toFloat(loader->svgParse, value, SvgParserLengthType::Vertical); + doc->viewFlag = (SvgViewFlag)((uint32_t)doc->viewFlag | (uint32_t)SvgViewFlag::Height); } else if (!strcmp(key, "viewBox")) { if (_parseNumber(&value, &doc->vx)) { if (_parseNumber(&value, &doc->vy)) { if (_parseNumber(&value, &doc->vw)) { - _parseNumber(&value, &doc->vh); - loader->svgParse->global.h = doc->vh; + if (_parseNumber(&value, &doc->vh)) { + doc->viewFlag = (SvgViewFlag)((uint32_t)doc->viewFlag | (uint32_t)SvgViewFlag::Viewbox); + loader->svgParse->global.h = doc->vh; + } + loader->svgParse->global.w = doc->vw; } - loader->svgParse->global.w = doc->vw; + loader->svgParse->global.y = doc->vy; } - loader->svgParse->global.y = doc->vy; + loader->svgParse->global.x = doc->vx; + } + if (((uint32_t)doc->viewFlag & (uint32_t)SvgViewFlag::Viewbox) && (doc->vw < 0.0f || doc->vh < 0.0f)) { + doc->viewFlag = (SvgViewFlag)((uint32_t)doc->viewFlag & ~(uint32_t)SvgViewFlag::Viewbox); + TVGLOG("SVG", "Negative values of the width and/or height - the attribute invalidated."); + } + if (!((uint32_t)doc->viewFlag & (uint32_t)SvgViewFlag::Viewbox)) { + loader->svgParse->global.x = loader->svgParse->global.y = 0.0f; + loader->svgParse->global.w = loader->svgParse->global.h = 1.0f; } - loader->svgParse->global.x = doc->vx; } else if (!strcmp(key, "preserveAspectRatio")) { _parseAspectRatio(&value, &doc->align, &doc->meetOrSlice); } else if (!strcmp(key, "style")) { @@ -1289,22 +1301,22 @@ static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const cha if (!loader->svgParse->node) return nullptr; SvgDocNode* doc = &(loader->svgParse->node->node.doc); - loader->svgParse->global.w = 0; - loader->svgParse->global.h = 0; + loader->svgParse->global.w = 1.0f; + loader->svgParse->global.h = 1.0f; doc->align = AspectRatioAlign::XMidYMid; doc->meetOrSlice = AspectRatioMeetOrSlice::Meet; + doc->viewFlag = SvgViewFlag::None; func(buf, bufLength, _attrParseSvgNode, loader); - if (loader->svgParse->global.w == 0) { - if (doc->w < FLT_EPSILON) loader->svgParse->global.w = 1; - else loader->svgParse->global.w = doc->w; - } - if (loader->svgParse->global.h == 0) { - if (doc->h < FLT_EPSILON) loader->svgParse->global.h = 1; - else loader->svgParse->global.h = doc->h; + if (!((uint32_t)doc->viewFlag & (uint32_t)SvgViewFlag::Viewbox)) { + if ((uint32_t)doc->viewFlag & (uint32_t)SvgViewFlag::Width) { + loader->svgParse->global.w = doc->w; + } + if ((uint32_t)doc->viewFlag & (uint32_t)SvgViewFlag::Height) { + loader->svgParse->global.h = doc->h; + } } - return loader->svgParse->node; } @@ -3192,7 +3204,7 @@ void SvgLoader::run(unsigned tid) _updateStyle(loaderData.doc, nullptr); } - root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, align, meetOrSlice, svgPath); + root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, align, meetOrSlice, svgPath, viewFlag); } @@ -3205,28 +3217,37 @@ bool SvgLoader::header() if (!loaderData.svgParse) return false; loaderData.svgParse->flags = SvgStopStyleFlags::StopDefault; + viewFlag = SvgViewFlag::None; simpleXmlParse(content, size, true, _svgLoaderParserForValidCheck, &(loaderData)); if (loaderData.doc && loaderData.doc->type == SvgNodeType::Doc) { - //Return the brief resource info such as viewbox: - vx = loaderData.doc->node.doc.vx; - vy = loaderData.doc->node.doc.vy; - w = vw = loaderData.doc->node.doc.vw; - h = vh = loaderData.doc->node.doc.vh; + viewFlag = loaderData.doc->node.doc.viewFlag; + align = loaderData.doc->node.doc.align; + meetOrSlice = loaderData.doc->node.doc.meetOrSlice; + w = 1.0f; + h = 1.0f; - //Override size - if (loaderData.doc->node.doc.w > 0) { + //Return the brief resource info such as viewbox: + if ((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Width) { w = loaderData.doc->node.doc.w; - if (vw < FLT_EPSILON) vw = w; } - if (loaderData.doc->node.doc.h > 0) { + if ((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Height) { h = loaderData.doc->node.doc.h; - if (vh < FLT_EPSILON) vh = h; } - - align = loaderData.doc->node.doc.align; - meetOrSlice = loaderData.doc->node.doc.meetOrSlice; + //Override size + if ((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Viewbox) { + vx = loaderData.doc->node.doc.vx; + vy = loaderData.doc->node.doc.vy; + vw = loaderData.doc->node.doc.vw; + vh = loaderData.doc->node.doc.vh; + + if (!((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Width)) w = vw; + if (!((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Height)) h = vh; + } else { + vw = w; + vh = h; + } } else { TVGLOG("SVG", "No SVG File. There is no "); return false; @@ -3292,6 +3313,12 @@ bool SvgLoader::read() { if (!content || size == 0) return false; + if (((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Viewbox) && + (fabsf(vw) <= FLT_EPSILON || fabsf(vh) <= FLT_EPSILON)) { + TVGLOG("SVG", "The width and/or height set to 0 - rendering disabled."); + return false; + } + TaskScheduler::request(this); return true; diff --git a/src/loaders/svg/tvgSvgLoader.h b/src/loaders/svg/tvgSvgLoader.h index fc42e5b..5c74184 100644 --- a/src/loaders/svg/tvgSvgLoader.h +++ b/src/loaders/svg/tvgSvgLoader.h @@ -51,6 +51,7 @@ public: unique_ptr paint() override; private: + SvgViewFlag viewFlag = SvgViewFlag::None; AspectRatioAlign align = AspectRatioAlign::XMidYMid; AspectRatioMeetOrSlice meetOrSlice = AspectRatioMeetOrSlice::Meet; diff --git a/src/loaders/svg/tvgSvgLoaderCommon.h b/src/loaders/svg/tvgSvgLoaderCommon.h index 9cb78de..ebcbf01 100644 --- a/src/loaders/svg/tvgSvgLoaderCommon.h +++ b/src/loaders/svg/tvgSvgLoaderCommon.h @@ -146,6 +146,14 @@ enum class SvgParserLengthType Other }; +enum class SvgViewFlag +{ + None = 0x0, + Width = 0x01, //viewPort width + Height = 0x02, //viewPort height + Viewbox = 0x04 //viewBox x,y,w,h - used only if all 4 are correctly set +}; + enum class AspectRatioAlign { None, @@ -174,6 +182,7 @@ struct SvgDocNode float vy; float vw; float vh; + SvgViewFlag viewFlag; SvgNode* defs; SvgNode* style; AspectRatioAlign align; diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index 5e97b74..f00b373 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -66,7 +66,6 @@ struct Box float x, y, w, h; }; - static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath); static unique_ptr _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth, bool* isMaskWhite = nullptr); @@ -755,11 +754,24 @@ static unique_ptr _sceneBuildHelper(const SvgNode* node, const Box& vBox, } +static void _applySvgViewFlag(const Scene* scene, float& vx, float& vy, float& vw, float& vh, float& w, float& h, SvgViewFlag viewFlag) +{ + if (!((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Viewbox)) { + scene->bounds(nullptr, nullptr, &vw, &vh, false); + vx = 0.0f; + vy = 0.0f; + if ((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Width) vw = w; + if ((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Height) vh = h; + } + if (!((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Width)) w = vw; + if (!((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Height)) h = vh; +} + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ -unique_ptr svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath) +unique_ptr svgSceneBuild(SvgNode* node, float& vx, float& vy, float& vw, float& vh, float& w, float& h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath, SvgViewFlag viewFlag) { //TODO: aspect ratio is valid only if viewBox was set @@ -767,9 +779,10 @@ unique_ptr svgSceneBuild(SvgNode* node, float vx, float vy, float vw, flo Box vBox = {vx, vy, vw, vh}; auto docNode = _sceneBuildHelper(node, vBox, svgPath, false, 0); + _applySvgViewFlag(docNode.get(), vx, vy, vw, vh, w, h, viewFlag); if (!mathEqual(w, vw) || !mathEqual(h, vh)) { - Matrix m = _calculateAspectRatioMatrix(align, meetOrSlice, w, h, vBox); + Matrix m = _calculateAspectRatioMatrix(align, meetOrSlice, w, h, {vx, vy, vw, vh}); docNode->transform(m); } else if (!mathZero(vx) || !mathZero(vy)) { docNode->translate(-vx, -vy); diff --git a/src/loaders/svg/tvgSvgSceneBuilder.h b/src/loaders/svg/tvgSvgSceneBuilder.h index b4fbcf1..0de8c9a 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.h +++ b/src/loaders/svg/tvgSvgSceneBuilder.h @@ -25,6 +25,6 @@ #include "tvgCommon.h" -unique_ptr svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath); +unique_ptr svgSceneBuild(SvgNode* node, float& vx, float& vy, float& vw, float& vh, float& w, float& h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath, SvgViewFlag viewFlag); #endif //_TVG_SVG_SCENE_BUILDER_H_ -- 2.7.4 From c266abaf3d093696f817d2bffcd840f8826fbf70 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Thu, 16 Mar 2023 00:47:11 +0100 Subject: [PATCH 12/16] svg_loader: handling zero width/height viewbox For svgs with the width and/or height value set to zero rendering was disabled - the load api return Result:Unknown and draw - Result::InsufficientCondition. Now an empty scene is added, so that both, load and draw, return Result::Success. Change-Id: I3b47acea33f49eca8d0d213a47d88cd57146027b Signed-off-by: jykeon --- src/loaders/svg/tvgSvgLoader.cpp | 8 +++++++- src/loaders/svg/tvgSvgLoader.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index e05aa49..1365663 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -3186,6 +3186,12 @@ SvgLoader::~SvgLoader() void SvgLoader::run(unsigned tid) { + //According to the SVG standard the value of the width/height of the viewbox set to 0 disables rendering + if (renderingDisabled) { + root = Scene::gen(); + return; + } + if (!simpleXmlParse(content, size, true, _svgLoaderParser, &(loaderData))) return; if (loaderData.doc) { @@ -3316,7 +3322,7 @@ bool SvgLoader::read() if (((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Viewbox) && (fabsf(vw) <= FLT_EPSILON || fabsf(vh) <= FLT_EPSILON)) { TVGLOG("SVG", "The width and/or height set to 0 - rendering disabled."); - return false; + renderingDisabled = true; } TaskScheduler::request(this); diff --git a/src/loaders/svg/tvgSvgLoader.h b/src/loaders/svg/tvgSvgLoader.h index 5c74184..43882b7 100644 --- a/src/loaders/svg/tvgSvgLoader.h +++ b/src/loaders/svg/tvgSvgLoader.h @@ -54,6 +54,7 @@ private: SvgViewFlag viewFlag = SvgViewFlag::None; AspectRatioAlign align = AspectRatioAlign::XMidYMid; AspectRatioMeetOrSlice meetOrSlice = AspectRatioMeetOrSlice::Meet; + bool renderingDisabled = false; bool header(); void clear(); -- 2.7.4 From e96f908b956c75e9b31173d1b74fdb4b89b284d8 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Tue, 21 Mar 2023 23:27:00 +0100 Subject: [PATCH 13/16] svg_loader: invalid strokes' width set to zero In case the "stroke" attrib is set to "none", the width of the stroke is set to zero. Thanks to that it isn't taken into account while establishing the bounds of the shape. Change-Id: I65cfd52075b72bebe9f9f4d24d54cde2e1dca99c Signed-off-by: jykeon --- src/loaders/svg/tvgSvgSceneBuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index f00b373..50097e4 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -346,7 +346,7 @@ static void _applyProperty(SvgNode* node, Shape* vg, const Box& vBox, const stri //If stroke property is nullptr then do nothing if (style->stroke.paint.none) { - //Do nothing + vg->stroke(0.0f); } else if (style->stroke.paint.gradient) { Box bBox = vBox; if (!style->stroke.paint.gradient->userSpace) bBox = _boundingBox(vg); -- 2.7.4 From c8877f836d6680b62694b466a24fe489e9c1bc72 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Sat, 25 Mar 2023 10:56:59 +0900 Subject: [PATCH 14/16] common/accessor: fix uninitialized field. --warnings. Change-Id: I94f6e5892985aa2093b9dbc9b89e0ac267370884 Signed-off-by: jykeon --- src/lib/tvgAccessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/tvgAccessor.cpp b/src/lib/tvgAccessor.cpp index 0add721..753315c 100644 --- a/src/lib/tvgAccessor.cpp +++ b/src/lib/tvgAccessor.cpp @@ -74,7 +74,7 @@ Accessor::~Accessor() } -Accessor::Accessor() +Accessor::Accessor() : pImpl(nullptr) { } -- 2.7.4 From 08254330fb152f9749328b90c234bfa72935f44d Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Sun, 26 Mar 2023 00:16:40 +0100 Subject: [PATCH 15/16] sw_engine: fix safety check An incorrect check introduced in bf07eb11f25909d2c73aa7e5baeb01a80af384d0 as a solution of the #1327 issue. As a consequence rotated images were not drawn. Fixed. Change-Id: I23a9842aff5c2b8463e9ee005932257eda6cb843 Signed-off-by: jykeon --- src/lib/sw_engine/tvgSwRasterTexmap.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/sw_engine/tvgSwRasterTexmap.h b/src/lib/sw_engine/tvgSwRasterTexmap.h index 40d4037..b01c8c3 100644 --- a/src/lib/sw_engine/tvgSwRasterTexmap.h +++ b/src/lib/sw_engine/tvgSwRasterTexmap.h @@ -548,7 +548,7 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans) static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox* region, uint32_t opacity, uint32_t (*blendMethod)(uint32_t)) { //Exceptions: No dedicated drawing area? - if (!image->rle || (!region && image->rle->size == 0)) return false; + if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false; /* Prepare vertices. shift XY coordinates to match the sub-pixeling technique. */ @@ -605,7 +605,7 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, const Polygon* triangles, const uint32_t triangleCount, const Matrix* transform, const SwBBox* region, uint32_t opacity, uint32_t (*blendMethod)(uint32_t)) { //Exceptions: No dedicated drawing area? - if (!image->rle || (!region && image->rle->size == 0)) return false; + if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false; // Step polygons once to transform auto transformedTris = (Polygon*)malloc(sizeof(Polygon) * triangleCount); -- 2.7.4 From 295dd97bc2fbdf8885f4bd18f6100ef2274816ed Mon Sep 17 00:00:00 2001 From: "joogab.yun" Date: Fri, 31 Mar 2023 10:35:04 +0900 Subject: [PATCH 16/16] [Tizen] Revert "svg_loader: preserveAspectRatio attrib handled according to the svg standard (#1249)" This reverts commit 795978177ab26c6ced98fd493f13e99a27c36397. There is a problem with the preserveAspectRatio patch, so it is temporarily reverted. This patch will go away once the preserveAspectRatio issue is resolved. Change-Id: I7287ee79b55a524859934d9e11b749f3b117688d --- src/lib/tvgLoadModule.h | 1 + src/loaders/svg/tvgSvgLoader.cpp | 83 +++++++++-------------- src/loaders/svg/tvgSvgLoader.h | 4 +- src/loaders/svg/tvgSvgLoaderCommon.h | 26 +------- src/loaders/svg/tvgSvgSceneBuilder.cpp | 118 +++++++++++---------------------- src/loaders/svg/tvgSvgSceneBuilder.h | 2 +- 6 files changed, 72 insertions(+), 162 deletions(-) diff --git a/src/lib/tvgLoadModule.h b/src/lib/tvgLoadModule.h index 4637c90..ad5d7b6 100644 --- a/src/lib/tvgLoadModule.h +++ b/src/lib/tvgLoadModule.h @@ -38,6 +38,7 @@ public: float vh = 0; float w = 0, h = 0; //default image size uint32_t colorSpace = SwCanvas::ARGB8888; + bool preserveAspect = true; //keep aspect ratio by default. virtual ~LoadModule() {} diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 1365663..83df473 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -119,48 +119,6 @@ static bool _parseNumber(const char** content, float* number) return true; } - -static constexpr struct -{ - AspectRatioAlign align; - const char* tag; -} alignTags[] = { - { AspectRatioAlign::XMinYMin, "xMinYMin" }, - { AspectRatioAlign::XMidYMin, "xMidYMin" }, - { AspectRatioAlign::XMaxYMin, "xMaxYMin" }, - { AspectRatioAlign::XMinYMid, "xMinYMid" }, - { AspectRatioAlign::XMidYMid, "xMidYMid" }, - { AspectRatioAlign::XMaxYMid, "xMaxYMid" }, - { AspectRatioAlign::XMinYMax, "xMinYMax" }, - { AspectRatioAlign::XMidYMax, "xMidYMax" }, - { AspectRatioAlign::XMaxYMax, "xMaxYMax" }, -}; - - -static void _parseAspectRatio(const char** content, AspectRatioAlign* align, AspectRatioMeetOrSlice* meetOrSlice) -{ - if (!strcmp(*content, "none")) { - *align = AspectRatioAlign::None; - return; - } - - for (unsigned int i = 0; i < sizeof(alignTags) / sizeof(alignTags[0]); i++) { - if (!strncmp(*content, alignTags[i].tag, 8)) { - *align = alignTags[i].align; - *content += 8; - *content = _skipSpace(*content, nullptr); - break; - } - } - - if (!strcmp(*content, "meet")) { - *meetOrSlice = AspectRatioMeetOrSlice::Meet; - } else if (!strcmp(*content, "slice")) { - *meetOrSlice = AspectRatioMeetOrSlice::Slice; - } -} - - /** * According to https://www.w3.org/TR/SVG/coords.html#Units */ @@ -858,7 +816,7 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value) loader->svgParse->global.w = loader->svgParse->global.h = 1.0f; } } else if (!strcmp(key, "preserveAspectRatio")) { - _parseAspectRatio(&value, &doc->align, &doc->meetOrSlice); + if (!strcmp(value, "none")) doc->preserveAspect = false; } else if (!strcmp(key, "style")) { return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); #ifdef THORVG_LOG_ENABLED @@ -1212,7 +1170,7 @@ static bool _attrParseSymbolNode(void* data, const char* key, const char* value) symbol->h = _toFloat(loader->svgParse, value, SvgParserLengthType::Vertical); symbol->hasHeight = true; } else if (!strcmp(key, "preserveAspectRatio")) { - _parseAspectRatio(&value, &symbol->align, &symbol->meetOrSlice); + if (!strcmp(value, "none")) symbol->preserveAspect = false; } else if (!strcmp(key, "overflow")) { if (!strcmp(value, "visible")) symbol->overflowVisible = true; } else { @@ -1304,9 +1262,8 @@ static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const cha loader->svgParse->global.w = 1.0f; loader->svgParse->global.h = 1.0f; - doc->align = AspectRatioAlign::XMidYMid; - doc->meetOrSlice = AspectRatioMeetOrSlice::Meet; doc->viewFlag = SvgViewFlag::None; + doc->preserveAspect = true; func(buf, bufLength, _attrParseSvgNode, loader); if (!((uint32_t)doc->viewFlag & (uint32_t)SvgViewFlag::Viewbox)) { @@ -1366,8 +1323,7 @@ static SvgNode* _createSymbolNode(SvgLoaderData* loader, SvgNode* parent, const if (!loader->svgParse->node) return nullptr; loader->svgParse->node->display = false; - loader->svgParse->node->node.symbol.align = AspectRatioAlign::XMidYMid; - loader->svgParse->node->node.symbol.meetOrSlice = AspectRatioMeetOrSlice::Meet; + loader->svgParse->node->node.symbol.preserveAspect = true; loader->svgParse->node->node.symbol.overflowVisible = false; loader->svgParse->node->node.symbol.hasViewBox = false; @@ -3210,7 +3166,7 @@ void SvgLoader::run(unsigned tid) _updateStyle(loaderData.doc, nullptr); } - root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, align, meetOrSlice, svgPath, viewFlag); + root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, preserveAspect, svgPath, viewFlag); } @@ -3229,8 +3185,6 @@ bool SvgLoader::header() if (loaderData.doc && loaderData.doc->type == SvgNodeType::Doc) { viewFlag = loaderData.doc->node.doc.viewFlag; - align = loaderData.doc->node.doc.align; - meetOrSlice = loaderData.doc->node.doc.meetOrSlice; w = 1.0f; h = 1.0f; @@ -3254,6 +3208,7 @@ bool SvgLoader::header() vw = w; vh = h; } + preserveAspect = loaderData.doc->node.doc.preserveAspect; } else { TVGLOG("SVG", "No SVG File. There is no "); return false; @@ -3308,9 +3263,31 @@ bool SvgLoader::resize(Paint* paint, float w, float h) auto sx = w / this->w; auto sy = h / this->h; - Matrix m = {sx, 0, 0, 0, sy, 0, 0, 0, 1}; - paint->transform(m); + if (preserveAspect) { + //Scale + auto scale = sx < sy ? sx : sy; + paint->scale(scale); + //Align + auto tx = 0.0f; + auto ty = 0.0f; + auto tw = this->w * scale; + auto th = this->h * scale; + if (tw > th) ty -= (h - th) * 0.5f; + else tx -= (w - tw) * 0.5f; + paint->translate(-tx, -ty); + } else { + //Align + auto tx = 0.0f; + auto ty = 0.0f; + auto tw = this->w * sx; + auto th = this->h * sy; + if (tw > th) ty -= (h - th) * 0.5f; + else tx -= (w - tw) * 0.5f; + + Matrix m = {sx, 0, -tx, 0, sy, -ty, 0, 0, 1}; + paint->transform(m); + } return true; } diff --git a/src/loaders/svg/tvgSvgLoader.h b/src/loaders/svg/tvgSvgLoader.h index 43882b7..4c4fe13 100644 --- a/src/loaders/svg/tvgSvgLoader.h +++ b/src/loaders/svg/tvgSvgLoader.h @@ -52,10 +52,8 @@ public: private: SvgViewFlag viewFlag = SvgViewFlag::None; - AspectRatioAlign align = AspectRatioAlign::XMidYMid; - AspectRatioMeetOrSlice meetOrSlice = AspectRatioMeetOrSlice::Meet; bool renderingDisabled = false; - + bool header(); void clear(); void run(unsigned tid) override; diff --git a/src/loaders/svg/tvgSvgLoaderCommon.h b/src/loaders/svg/tvgSvgLoaderCommon.h index ebcbf01..6fed159 100644 --- a/src/loaders/svg/tvgSvgLoaderCommon.h +++ b/src/loaders/svg/tvgSvgLoaderCommon.h @@ -154,26 +154,6 @@ enum class SvgViewFlag Viewbox = 0x04 //viewBox x,y,w,h - used only if all 4 are correctly set }; -enum class AspectRatioAlign -{ - None, - XMinYMin, - XMidYMin, - XMaxYMin, - XMinYMid, - XMidYMid, - XMaxYMid, - XMinYMax, - XMidYMax, - XMaxYMax -}; - -enum class AspectRatioMeetOrSlice -{ - Meet, - Slice -}; - struct SvgDocNode { float w; @@ -185,8 +165,7 @@ struct SvgDocNode SvgViewFlag viewFlag; SvgNode* defs; SvgNode* style; - AspectRatioAlign align; - AspectRatioMeetOrSlice meetOrSlice; + bool preserveAspect; }; struct SvgGNode @@ -202,8 +181,7 @@ struct SvgSymbolNode { float w, h; float vx, vy, vw, vh; - AspectRatioAlign align; - AspectRatioMeetOrSlice meetOrSlice; + bool preserveAspect; bool overflowVisible; bool hasViewBox; bool hasWidth; diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index 50097e4..4401f2c 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -558,80 +558,6 @@ static unique_ptr _imageBuildHelper(SvgNode* node, const Box& vBox, con } -static Matrix _calculateAspectRatioMatrix(AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, float width, float height, const Box& box) -{ - auto sx = width / box.w; - auto sy = height / box.h; - auto tvx = box.x * sx; - auto tvy = box.y * sy; - - if (align == AspectRatioAlign::None) - return {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1}; - - //Scale - if (meetOrSlice == AspectRatioMeetOrSlice::Meet) { - if (sx < sy) sy = sx; - else sx = sy; - } else { - if (sx < sy) sx = sy; - else sy = sx; - } - - //Align - tvx = box.x * sx; - tvy = box.y * sy; - auto tvw = box.w * sx; - auto tvh = box.h * sy; - - switch (align) { - case AspectRatioAlign::XMinYMin: { - break; - } - case AspectRatioAlign::XMidYMin: { - tvx -= (width - tvw) * 0.5f; - break; - } - case AspectRatioAlign::XMaxYMin: { - tvx -= width - tvw; - break; - } - case AspectRatioAlign::XMinYMid: { - tvy -= (height - tvh) * 0.5f; - break; - } - case AspectRatioAlign::XMidYMid: { - tvx -= (width - tvw) * 0.5f; - tvy -= (height - tvh) * 0.5f; - break; - } - case AspectRatioAlign::XMaxYMid: { - tvx -= width - tvw; - tvy -= (height - tvh) * 0.5f; - break; - } - case AspectRatioAlign::XMinYMax: { - tvy -= height - tvh; - break; - } - case AspectRatioAlign::XMidYMax: { - tvx -= (width - tvw) * 0.5f; - tvy -= height - tvh; - break; - } - case AspectRatioAlign::XMaxYMax: { - tvx -= width - tvw; - tvy -= height - tvh; - break; - } - default: { - break; - } - } - - return {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1}; -} - - static unique_ptr _useBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, int depth, bool* isMaskWhite) { unique_ptr finalScene; @@ -657,8 +583,20 @@ static unique_ptr _useBuildHelper(const SvgNode* node, const Box& vBox, c Matrix mViewBox = {1, 0, 0, 0, 1, 0, 0, 0, 1}; if ((!mathEqual(width, vw) || !mathEqual(height, vh)) && vw > 0 && vh > 0) { - Box box = {symbol.vx, symbol.vy, vw, vh}; - mViewBox = _calculateAspectRatioMatrix(symbol.align, symbol.meetOrSlice, width, height, box); + auto sx = width / vw; + auto sy = height / vh; + if (symbol.preserveAspect) { + if (sx < sy) sy = sx; + else sx = sy; + } + + auto tvx = symbol.vx * sx; + auto tvy = symbol.vy * sy; + auto tvw = vw * sx; + auto tvh = vh * sy; + tvy -= (symbol.h - tvh) * 0.5f; + tvx -= (symbol.w - tvw) * 0.5f; + mViewBox = {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1}; } else if (!mathZero(symbol.vx) || !mathZero(symbol.vy)) { mViewBox = {1, 0, -symbol.vx, 0, 1, -symbol.vy, 0, 0, 1}; } @@ -771,10 +709,8 @@ static void _applySvgViewFlag(const Scene* scene, float& vx, float& vy, float& v /* External Class Implementation */ /************************************************************************/ -unique_ptr svgSceneBuild(SvgNode* node, float& vx, float& vy, float& vw, float& vh, float& w, float& h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath, SvgViewFlag viewFlag) +unique_ptr svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, bool preserveAspect, const string& svgPath, SvgViewFlag viewFlag) { - //TODO: aspect ratio is valid only if viewBox was set - if (!node || (node->type != SvgNodeType::Doc)) return nullptr; Box vBox = {vx, vy, vw, vh}; @@ -782,8 +718,28 @@ unique_ptr svgSceneBuild(SvgNode* node, float& vx, float& vy, float& vw, _applySvgViewFlag(docNode.get(), vx, vy, vw, vh, w, h, viewFlag); if (!mathEqual(w, vw) || !mathEqual(h, vh)) { - Matrix m = _calculateAspectRatioMatrix(align, meetOrSlice, w, h, {vx, vy, vw, vh}); - docNode->transform(m); + auto sx = w / vw; + auto sy = h / vh; + + if (preserveAspect) { + //Scale + auto scale = sx < sy ? sx : sy; + docNode->scale(scale); + //Align + auto tvx = vx * scale; + auto tvy = vy * scale; + auto tvw = vw * scale; + auto tvh = vh * scale; + tvx -= (w - tvw) * 0.5f; + tvy -= (h - tvh) * 0.5f; + docNode->translate(-tvx, -tvy); + } else { + //Align + auto tvx = vx * sx; + auto tvy = vy * sy; + Matrix m = {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1}; + docNode->transform(m); + } } else if (!mathZero(vx) || !mathZero(vy)) { docNode->translate(-vx, -vy); } diff --git a/src/loaders/svg/tvgSvgSceneBuilder.h b/src/loaders/svg/tvgSvgSceneBuilder.h index 0de8c9a..4f4fe54 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.h +++ b/src/loaders/svg/tvgSvgSceneBuilder.h @@ -25,6 +25,6 @@ #include "tvgCommon.h" -unique_ptr svgSceneBuild(SvgNode* node, float& vx, float& vy, float& vw, float& vh, float& w, float& h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath, SvgViewFlag viewFlag); +unique_ptr svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, bool preserveAspect, const string& svgPath, SvgViewFlag viewFlag); #endif //_TVG_SVG_SCENE_BUILDER_H_ -- 2.7.4