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() {}
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
*/
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
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 {
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)) {
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;
_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);
}
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;
vw = w;
vh = h;
}
+ preserveAspect = loaderData.doc->node.doc.preserveAspect;
} else {
TVGLOG("SVG", "No SVG File. There is no <svg/>");
return false;
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;
}
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;
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;
SvgViewFlag viewFlag;
SvgNode* defs;
SvgNode* style;
- AspectRatioAlign align;
- AspectRatioMeetOrSlice meetOrSlice;
+ bool preserveAspect;
};
struct SvgGNode
{
float w, h;
float vx, vy, vw, vh;
- AspectRatioAlign align;
- AspectRatioMeetOrSlice meetOrSlice;
+ bool preserveAspect;
bool overflowVisible;
bool hasViewBox;
bool hasWidth;
}
-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<Scene> _useBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, int depth, bool* isMaskWhite)
{
unique_ptr<Scene> finalScene;
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};
}
/* 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(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};
_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);
}
#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(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_