diff --git a/src/lib/tvgPicture.cpp b/src/lib/tvgPicture.cpp index 2b1d0f54..0af5430f 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 6ef23132..4c9b2a17 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 073f0cfc..e05aa490 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 (!((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; + } } - if (loader->svgParse->global.h == 0) { - if (doc->h < FLT_EPSILON) loader->svgParse->global.h = 1; - else 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; - - //Override size - if (loaderData.doc->node.doc.w > 0) { - w = loaderData.doc->node.doc.w; - if (vw < FLT_EPSILON) vw = w; - } - if (loaderData.doc->node.doc.h > 0) { - h = loaderData.doc->node.doc.h; - if (vh < FLT_EPSILON) vh = h; - } - + viewFlag = loaderData.doc->node.doc.viewFlag; align = loaderData.doc->node.doc.align; meetOrSlice = loaderData.doc->node.doc.meetOrSlice; + w = 1.0f; + h = 1.0f; + + //Return the brief resource info such as viewbox: + if ((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Width) { + w = loaderData.doc->node.doc.w; + } + if ((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Height) { + h = loaderData.doc->node.doc.h; + } + //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 fc42e5bd..5c74184e 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 9cb78dea..ebcbf018 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 5e97b741..f00b3733 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 b4fbcf16..0de8c9a7 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_