svg_loader: fix memory violation.

LoadModule data is designed to be returned to the user's call.
and should not be writable in Task::run()

@Issue: https://github.com/thorvg/thorvg/issues/1409
This commit is contained in:
Hermet Park 2023-05-03 21:51:54 +09:00 committed by Hermet Park
parent 92346c4119
commit f82c274444
4 changed files with 103 additions and 71 deletions

View file

@ -862,14 +862,16 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value)
if (!strcmp(key, "width")) {
doc->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
if (strstr(value, "%")) {
doc->sw = svgUtilStrtof(value, nullptr) / 100.0f;
doc->viewFlag = (doc->viewFlag | SvgViewFlag::WidthInPercent);
doc->w /= 100.0f;
} else {
doc->viewFlag = (doc->viewFlag | SvgViewFlag::Width);
}
} else if (!strcmp(key, "height")) {
doc->h = _toFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
if (strstr(value, "%")) {
doc->sh = svgUtilStrtof(value, nullptr) / 100.0f;
doc->viewFlag = (doc->viewFlag | SvgViewFlag::HeightInPercent);
doc->h /= 100.0f;
} else {
doc->viewFlag = (doc->viewFlag | SvgViewFlag::Height);
}
@ -1355,8 +1357,6 @@ static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const cha
doc->align = AspectRatioAlign::XMidYMid;
doc->meetOrSlice = AspectRatioMeetOrSlice::Meet;
doc->viewFlag = SvgViewFlag::None;
doc->sw = 1.0f;
doc->sh = 1.0f;
func(buf, bufLength, _attrParseSvgNode, loader);
if (!(doc->viewFlag & SvgViewFlag::Viewbox)) {
@ -3502,7 +3502,7 @@ void SvgLoader::run(unsigned tid)
if (loaderData.gradients.count > 0) _updateGradient(&loaderData, loaderData.doc, &loaderData.gradients);
if (defs) _updateGradient(&loaderData, loaderData.doc, &defs->node.defs.gradients);
}
root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, align, meetOrSlice, svgPath, viewFlag);
root = svgSceneBuild(loaderData, {vx, vy, vw, vh}, w, h, align, meetOrSlice, svgPath, viewFlag);
}
@ -3523,43 +3523,70 @@ bool SvgLoader::header()
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 (viewFlag & SvgViewFlag::Width) {
w = loaderData.doc->node.doc.w;
}
if (viewFlag & SvgViewFlag::Height) {
h = loaderData.doc->node.doc.h;
}
//Override size
if (viewFlag & 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 (!(viewFlag & SvgViewFlag::Width)) {
w = vw * loaderData.doc->node.doc.sw;
loaderData.doc->node.doc.sw = 1.0f;
if (viewFlag & SvgViewFlag::Width) w = loaderData.doc->node.doc.w;
else {
w = loaderData.doc->node.doc.vw;
if (viewFlag & SvgViewFlag::WidthInPercent) {
w *= loaderData.doc->node.doc.w;
viewFlag = (viewFlag ^ SvgViewFlag::WidthInPercent);
}
viewFlag = (viewFlag | SvgViewFlag::Width);
}
if (!(viewFlag & SvgViewFlag::Height)) {
h = vh * loaderData.doc->node.doc.sh;
loaderData.doc->node.doc.sh = 1.0f;
if (viewFlag & SvgViewFlag::Height) h = loaderData.doc->node.doc.h;
else {
h = loaderData.doc->node.doc.vh;
if (viewFlag & SvgViewFlag::HeightInPercent) {
h *= loaderData.doc->node.doc.h;
viewFlag = (viewFlag ^ SvgViewFlag::HeightInPercent);
}
viewFlag = (viewFlag | SvgViewFlag::Height);
}
//In case no viewbox and width/height data is provided the completion of loading
//has to be forced, in order to establish this data based on the whole picture.
} else {
vw = w;
vh = h;
//Before loading, set default viewbox & size if they are empty
vx = vy = 0.0f;
if (viewFlag & SvgViewFlag::Width) {
vw = w = loaderData.doc->node.doc.w;
} else {
vw = 1.0f;
if (viewFlag & SvgViewFlag::WidthInPercent) {
w = loaderData.doc->node.doc.w;
} else w = 1.0f;
}
if (viewFlag & SvgViewFlag::Height) {
vh = h = loaderData.doc->node.doc.h;
} else {
vh = 1.0f;
if (viewFlag & SvgViewFlag::HeightInPercent) {
h = loaderData.doc->node.doc.h;
} else h = 1.0f;
}
run(0);
//Override viewbox & size again after svg loading.
vx = loaderData.doc->node.doc.vx;
vy = loaderData.doc->node.doc.vy;
vw = loaderData.doc->node.doc.vw;
vh = loaderData.doc->node.doc.vh;
w = loaderData.doc->node.doc.w;
h = loaderData.doc->node.doc.h;
}
} else {
TVGLOG("SVG", "No SVG File. There is no <svg/>");
return false;
return true;
}
return true;
TVGLOG("SVG", "No SVG File. There is no <svg/>");
return false;
}
@ -3624,13 +3651,10 @@ bool SvgLoader::read()
renderingDisabled = true;
}
TaskScheduler::request(this);
//the loading has been already completed in header()
if (root) return true;
//In case no viewbox and width/height data is provided the completion of loading
//has to be forced, in order to establish this data based on the whole picture bounding box.
if (!(viewFlag & SvgViewFlag::Viewbox) && (!(viewFlag & SvgViewFlag::Width) || !(viewFlag & SvgViewFlag::Height))) {
this->done();
}
TaskScheduler::request(this);
return true;
}

View file

@ -219,7 +219,9 @@ 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
Viewbox = 0x04, //viewBox x,y,w,h - used only if all 4 are correctly set
WidthInPercent = 0x08,
HeightInPercent = 0x10
};
constexpr bool operator &(SvgViewFlag a, SvgViewFlag b)
@ -232,6 +234,11 @@ constexpr SvgViewFlag operator |(SvgViewFlag a, SvgViewFlag b)
return SvgViewFlag(int(a) | int(b));
}
constexpr SvgViewFlag operator ^(SvgViewFlag a, SvgViewFlag b)
{
return SvgViewFlag(int(a) ^ int(b));
}
enum class AspectRatioAlign
{
None,
@ -254,14 +261,12 @@ enum class AspectRatioMeetOrSlice
struct SvgDocNode
{
float w;
float h;
float w; //unit: point or in percentage see: SvgViewFlag
float h; //unit: point or in percentage see: SvgViewFlag
float vx;
float vy;
float vw;
float vh;
float sw;
float sh;
SvgViewFlag viewFlag;
SvgNode* defs;
SvgNode* style;
@ -548,6 +553,11 @@ struct SvgLoaderData
bool style = false;
};
struct Box
{
float x, y, w, h;
};
/*
* https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtod-l-wcstod-wcstod-l?view=vs-2017
*

View file

@ -61,11 +61,6 @@
/* Internal Class Implementation */
/************************************************************************/
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);
@ -763,49 +758,45 @@ 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)
static void _updateInvalidViewSize(const Scene* scene, Box& vBox, float& w, float& h, SvgViewFlag viewFlag)
{
bool noViewbox = !(viewFlag & SvgViewFlag::Viewbox);
bool noWidth = !(viewFlag & SvgViewFlag::Width);
bool noHeight = !(viewFlag & SvgViewFlag::Height);
bool validWidth = (viewFlag & SvgViewFlag::Width);
bool validHeight = (viewFlag & SvgViewFlag::Height);
if (noViewbox) {
float x, y;
scene->bounds(&x, &y, &vw, &vh, false);
if (noWidth && noHeight) {
vx = x;
vy = y;
} else {
vw = noWidth ? vw : w;
vh = noHeight ? vh : h;
}
float x, y;
scene->bounds(&x, &y, &vBox.w, &vBox.h, false);
if (!validWidth && !validHeight) {
vBox.x = x;
vBox.y = y;
} else {
if (validWidth) vBox.w = w;
if (validHeight) vBox.h = h;
}
w = noWidth ? vw : w;
h = noHeight ? vh : h;
//the size would have 1x1 or percentage values.
if (!validWidth) w *= vBox.w;
if (!validHeight) h *= vBox.h;
}
/************************************************************************/
/* 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, SvgViewFlag viewFlag)
unique_ptr<Scene> svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath, SvgViewFlag viewFlag)
{
//TODO: aspect ratio is valid only if viewBox was set
if (!node || (node->type != SvgNodeType::Doc)) return nullptr;
if (!loaderData.doc || (loaderData.doc->type != SvgNodeType::Doc)) return nullptr;
Box vBox = {vx, vy, vw, vh};
auto docNode = _sceneBuildHelper(node, vBox, svgPath, false, 0);
auto docNode = _sceneBuildHelper(loaderData.doc, vBox, svgPath, false, 0);
_applySvgViewFlag(docNode.get(), vx, vy, vw, vh, w, h, viewFlag);
w *= node->node.doc.sw;
h *= node->node.doc.sh;
if (!(viewFlag & SvgViewFlag::Viewbox)) _updateInvalidViewSize(docNode.get(), vBox, w, h, viewFlag);
if (!mathEqual(w, vw) || !mathEqual(h, vh)) {
Matrix m = _calculateAspectRatioMatrix(align, meetOrSlice, w, h, {vx, vy, vw, vh});
if (!mathEqual(w, vBox.w) || !mathEqual(h, vBox.h)) {
Matrix m = _calculateAspectRatioMatrix(align, meetOrSlice, w, h, vBox);
docNode->transform(m);
} else if (!mathZero(vx) || !mathZero(vy)) {
docNode->translate(-vx, -vy);
} else if (!mathZero(vBox.x) || !mathZero(vBox.y)) {
docNode->translate(-vBox.x, -vBox.y);
}
auto viewBoxClip = Shape::gen();
@ -819,5 +810,12 @@ unique_ptr<Scene> svgSceneBuild(SvgNode* node, float& vx, float& vy, float& vw,
auto root = Scene::gen();
root->push(move(compositeLayer));
loaderData.doc->node.doc.vx = vBox.x;
loaderData.doc->node.doc.vy = vBox.y;
loaderData.doc->node.doc.vw = vBox.w;
loaderData.doc->node.doc.vh = vBox.h;
loaderData.doc->node.doc.w = w;
loaderData.doc->node.doc.h = h;
return root;
}

View file

@ -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, SvgViewFlag viewFlag);
unique_ptr<Scene> svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath, SvgViewFlag viewFlag);
#endif //_TVG_SVG_SCENE_BUILDER_H_