mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 13:43:43 +00:00
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
This commit is contained in:
parent
c9657e3e68
commit
9e8980a1f7
7 changed files with 93 additions and 37 deletions
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -831,13 +831,16 @@ 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);
|
||||
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;
|
||||
|
@ -845,6 +848,15 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value)
|
|||
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 <viewBox> 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;
|
||||
}
|
||||
} 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 <svg/>");
|
||||
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 <viewBox> width and/or height set to 0 - rendering disabled.");
|
||||
return false;
|
||||
}
|
||||
|
||||
TaskScheduler::request(this);
|
||||
|
||||
return true;
|
||||
|
|
|
@ -51,6 +51,7 @@ public:
|
|||
unique_ptr<Paint> paint() override;
|
||||
|
||||
private:
|
||||
SvgViewFlag viewFlag = SvgViewFlag::None;
|
||||
AspectRatioAlign align = AspectRatioAlign::XMidYMid;
|
||||
AspectRatioMeetOrSlice meetOrSlice = AspectRatioMeetOrSlice::Meet;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth, bool* isMaskWhite = nullptr);
|
||||
|
||||
|
@ -755,11 +754,24 @@ static unique_ptr<Scene> _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<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath)
|
||||
unique_ptr<Scene> 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<Scene> 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);
|
||||
|
|
|
@ -25,6 +25,6 @@
|
|||
|
||||
#include "tvgCommon.h"
|
||||
|
||||
unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath);
|
||||
unique_ptr<Scene> 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_
|
||||
|
|
Loading…
Add table
Reference in a new issue