mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 13:43:43 +00:00
svg: added filter and gaussian blur nodes support
@Issue: https://github.com/thorvg/thorvg/issues/1367
This commit is contained in:
parent
b79d12a04d
commit
fbdc958623
3 changed files with 323 additions and 14 deletions
|
@ -1088,6 +1088,17 @@ static void _handleMaskAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, con
|
|||
}
|
||||
|
||||
|
||||
static void _handleFilterAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
|
||||
{
|
||||
SvgStyleProperty* style = node->style;
|
||||
int len = strlen(value);
|
||||
if (len >= 3 && !strncmp(value, "url", 3)) {
|
||||
if (style->filter.url) free(style->filter.url);
|
||||
style->filter.url = _idFromUrl((const char*)(value + 3));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void _handleMaskTypeAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
|
||||
{
|
||||
node->node.mask.type = _toMaskType(value);
|
||||
|
@ -1166,7 +1177,8 @@ static constexpr struct
|
|||
STYLE_DEF(mask, Mask, SvgStyleFlags::Mask),
|
||||
STYLE_DEF(mask-type, MaskType, SvgStyleFlags::MaskType),
|
||||
STYLE_DEF(display, Display, SvgStyleFlags::Display),
|
||||
STYLE_DEF(paint-order, PaintOrder, SvgStyleFlags::PaintOrder)
|
||||
STYLE_DEF(paint-order, PaintOrder, SvgStyleFlags::PaintOrder),
|
||||
STYLE_DEF(filter, Filter, SvgStyleFlags::Filter)
|
||||
};
|
||||
|
||||
|
||||
|
@ -1240,6 +1252,8 @@ static bool _attrParseGNode(void* data, const char* key, const char* value)
|
|||
_handleClipPathAttr(loader, node, value);
|
||||
} else if (!strcmp(key, "mask")) {
|
||||
_handleMaskAttr(loader, node, value);
|
||||
} else if (!strcmp(key, "filter")) {
|
||||
_handleFilterAttr(loader, node, value);
|
||||
} else {
|
||||
return _parseStyleAttr(loader, key, value, false);
|
||||
}
|
||||
|
@ -1343,6 +1357,108 @@ static bool _attrParseSymbolNode(void* data, const char* key, const char* value)
|
|||
}
|
||||
|
||||
|
||||
static constexpr struct
|
||||
{
|
||||
const char* tag;
|
||||
SvgParserLengthType type;
|
||||
int sz;
|
||||
size_t offset;
|
||||
} boxTags[] = {
|
||||
{"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(Box, x)},
|
||||
{"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(Box, y)},
|
||||
{"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(Box, w)},
|
||||
{"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(Box, h)}
|
||||
};
|
||||
|
||||
|
||||
static bool _parseBox(const char* key, const char* value, Box* box, bool (&isPercentage)[4])
|
||||
{
|
||||
auto array = (unsigned char*)box;
|
||||
int sz = strlen(key);
|
||||
for (unsigned int i = 0; i < sizeof(boxTags) / sizeof(boxTags[0]); i++) {
|
||||
if (boxTags[i].sz - 1 == sz && !strncmp(boxTags[i].tag, key, sz)) {
|
||||
*(float*)(array + boxTags[i].offset) = _gradientToFloat(nullptr, value, isPercentage[i]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static void _recalcBox(const SvgLoaderData* loader, Box* box, bool (&isPercentage)[4])
|
||||
{
|
||||
auto array = (unsigned char*)box;
|
||||
for (unsigned int i = 0; i < sizeof(boxTags) / sizeof(boxTags[0]); i++) {
|
||||
if (!isPercentage[i]) continue;
|
||||
if (boxTags[i].type == SvgParserLengthType::Horizontal) *(float*)(array + boxTags[i].offset) *= loader->svgParse->global.w;
|
||||
else *(float*)(array + boxTags[i].offset) *= loader->svgParse->global.h;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool _attrParseFilterNode(void* data, const char* key, const char* value)
|
||||
{
|
||||
SvgLoaderData* loader = (SvgLoaderData*)data;
|
||||
SvgNode* node = loader->svgParse->node;
|
||||
SvgFilterNode* filter = &node->node.filter;
|
||||
|
||||
_parseBox(key, value, &filter->box, filter->isPercentage);
|
||||
|
||||
if (!strcmp(key, "id")) {
|
||||
if (node->id && value) free(node->id);
|
||||
node->id = _copyId(value);
|
||||
} else if (!strcmp(key, "primitiveUnits")) {
|
||||
if (!strcmp(value, "objectBoundingBox")) filter->primitiveUserSpace = false;
|
||||
} else if (!strcmp(key, "filterUnits")) {
|
||||
if (!strcmp(value, "userSpaceOnUse")) filter->filterUserSpace = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void _parseGaussianBlurStdDeviation(const char** content, float* x, float* y)
|
||||
{
|
||||
auto str = *content;
|
||||
char* end = nullptr;
|
||||
float deviation[2] = {0, 0};
|
||||
int n = 0;
|
||||
|
||||
while (*str && n < 2) {
|
||||
str = _skipComma(str);
|
||||
auto parsedValue = strToFloat(str, &end);
|
||||
if (parsedValue < 0.0f) break;
|
||||
deviation[n++] = parsedValue;
|
||||
str = end;
|
||||
}
|
||||
|
||||
*x = deviation[0];
|
||||
*y = n == 1 ? deviation[0] : deviation[1];
|
||||
}
|
||||
|
||||
|
||||
static bool _attrParseGaussianBlurNode(void* data, const char* key, const char* value)
|
||||
{
|
||||
SvgLoaderData* loader = (SvgLoaderData*)data;
|
||||
SvgNode* node = loader->svgParse->node;
|
||||
SvgGaussianBlurNode* gaussianBlur = &node->node.gaussianBlur;
|
||||
|
||||
if (_parseBox(key, value, &gaussianBlur->box, gaussianBlur->isPercentage)) gaussianBlur->hasBox = true;
|
||||
|
||||
if (!strcmp(key, "id")) {
|
||||
if (node->id && value) free(node->id);
|
||||
node->id = _copyId(value);
|
||||
} else if (!strcmp(key, "stdDeviation")) {
|
||||
_parseGaussianBlurStdDeviation(&value, &gaussianBlur->stdDevX, &gaussianBlur->stdDevY);
|
||||
} else if (!strcmp(key, "edgeMode")) {
|
||||
if (!strcmp(value, "wrap")) gaussianBlur->edgeModeWrap = true;
|
||||
} else {
|
||||
return _parseStyleAttr(loader, key, value, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static SvgNode* _createNode(SvgNode* parent, SvgNodeType type)
|
||||
{
|
||||
SvgNode* node = (SvgNode*)calloc(1, sizeof(SvgNode));
|
||||
|
@ -1504,6 +1620,48 @@ static SvgNode* _createSymbolNode(SvgLoaderData* loader, SvgNode* parent, const
|
|||
}
|
||||
|
||||
|
||||
static SvgNode* _createGaussianBlurNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
|
||||
{
|
||||
loader->svgParse->node = _createNode(parent, SvgNodeType::GaussianBlur);
|
||||
if (!loader->svgParse->node) return nullptr;
|
||||
SvgGaussianBlurNode& gaussianBlur = loader->svgParse->node->node.gaussianBlur;
|
||||
|
||||
loader->svgParse->node->style->display = false;
|
||||
|
||||
gaussianBlur.stdDevX = 0.0f;
|
||||
gaussianBlur.stdDevY = 0.0f;
|
||||
gaussianBlur.box = {0.0f, 0.0f, 1.0f, 1.0f};
|
||||
for (auto& p : gaussianBlur.isPercentage) p = false;
|
||||
gaussianBlur.hasBox = false;
|
||||
gaussianBlur.edgeModeWrap = false;
|
||||
|
||||
func(buf, bufLength, _attrParseGaussianBlurNode, loader);
|
||||
|
||||
return loader->svgParse->node;
|
||||
}
|
||||
|
||||
|
||||
static SvgNode* _createFilterNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
|
||||
{
|
||||
loader->svgParse->node = _createNode(parent, SvgNodeType::Filter);
|
||||
if (!loader->svgParse->node) return nullptr;
|
||||
SvgFilterNode& filter = loader->svgParse->node->node.filter;
|
||||
|
||||
loader->svgParse->node->style->display = false;
|
||||
|
||||
filter.box = {-0.1f, -0.1f, 1.2f, 1.2f};
|
||||
for (auto& p : filter.isPercentage) p = false;
|
||||
filter.filterUserSpace = false;
|
||||
filter.primitiveUserSpace = true;
|
||||
|
||||
func(buf, bufLength, _attrParseFilterNode, loader);
|
||||
|
||||
if (filter.filterUserSpace) _recalcBox(loader, &filter.box, filter.isPercentage);
|
||||
|
||||
return loader->svgParse->node;
|
||||
}
|
||||
|
||||
|
||||
static bool _attrParsePathNode(void* data, const char* key, const char* value)
|
||||
{
|
||||
SvgLoaderData* loader = (SvgLoaderData*)data;
|
||||
|
@ -1520,6 +1678,8 @@ static bool _attrParsePathNode(void* data, const char* key, const char* value)
|
|||
_handleClipPathAttr(loader, node, value);
|
||||
} else if (!strcmp(key, "mask")) {
|
||||
_handleMaskAttr(loader, node, value);
|
||||
} else if (!strcmp(key, "filter")) {
|
||||
_handleFilterAttr(loader, node, value);
|
||||
} else if (!strcmp(key, "id")) {
|
||||
if (value) free(node->id);
|
||||
node->id = _copyId(value);
|
||||
|
@ -1582,6 +1742,8 @@ static bool _attrParseCircleNode(void* data, const char* key, const char* value)
|
|||
_handleClipPathAttr(loader, node, value);
|
||||
} else if (!strcmp(key, "mask")) {
|
||||
_handleMaskAttr(loader, node, value);
|
||||
} else if (!strcmp(key, "filter")) {
|
||||
_handleFilterAttr(loader, node, value);
|
||||
} else if (!strcmp(key, "id")) {
|
||||
if (value) free(node->id);
|
||||
node->id = _copyId(value);
|
||||
|
@ -1649,6 +1811,8 @@ static bool _attrParseEllipseNode(void* data, const char* key, const char* value
|
|||
_handleClipPathAttr(loader, node, value);
|
||||
} else if (!strcmp(key, "mask")) {
|
||||
_handleMaskAttr(loader, node, value);
|
||||
} else if (!strcmp(key, "filter")) {
|
||||
_handleFilterAttr(loader, node, value);
|
||||
} else {
|
||||
return _parseStyleAttr(loader, key, value, false);
|
||||
}
|
||||
|
@ -1698,6 +1862,8 @@ static bool _attrParsePolygonNode(void* data, const char* key, const char* value
|
|||
_handleClipPathAttr(loader, node, value);
|
||||
} else if (!strcmp(key, "mask")) {
|
||||
_handleMaskAttr(loader, node, value);
|
||||
} else if (!strcmp(key, "filter")) {
|
||||
_handleFilterAttr(loader, node, value);
|
||||
} else if (!strcmp(key, "id")) {
|
||||
if (value) free(node->id);
|
||||
node->id = _copyId(value);
|
||||
|
@ -1785,6 +1951,8 @@ static bool _attrParseRectNode(void* data, const char* key, const char* value)
|
|||
_handleClipPathAttr(loader, node, value);
|
||||
} else if (!strcmp(key, "mask")) {
|
||||
_handleMaskAttr(loader, node, value);
|
||||
} else if (!strcmp(key, "filter")) {
|
||||
_handleFilterAttr(loader, node, value);
|
||||
} else {
|
||||
ret = _parseStyleAttr(loader, key, value, false);
|
||||
}
|
||||
|
@ -1850,6 +2018,8 @@ static bool _attrParseLineNode(void* data, const char* key, const char* value)
|
|||
_handleClipPathAttr(loader, node, value);
|
||||
} else if (!strcmp(key, "mask")) {
|
||||
_handleMaskAttr(loader, node, value);
|
||||
} else if (!strcmp(key, "filter")) {
|
||||
_handleFilterAttr(loader, node, value);
|
||||
} else {
|
||||
return _parseStyleAttr(loader, key, value, false);
|
||||
}
|
||||
|
@ -1923,6 +2093,8 @@ static bool _attrParseImageNode(void* data, const char* key, const char* value)
|
|||
_handleClipPathAttr(loader, node, value);
|
||||
} else if (!strcmp(key, "mask")) {
|
||||
_handleMaskAttr(loader, node, value);
|
||||
} else if (!strcmp(key, "filter")) {
|
||||
_handleFilterAttr(loader, node, value);
|
||||
} else if (!strcmp(key, "transform")) {
|
||||
node->transform = _parseTransformationMatrix(value);
|
||||
} else {
|
||||
|
@ -2103,6 +2275,8 @@ static bool _attrParseTextNode(void* data, const char* key, const char* value)
|
|||
_handleClipPathAttr(loader, node, value);
|
||||
} else if (!strcmp(key, "mask")) {
|
||||
_handleMaskAttr(loader, node, value);
|
||||
} else if (!strcmp(key, "filter")) {
|
||||
_handleFilterAttr(loader, node, value);
|
||||
} else if (!strcmp(key, "id")) {
|
||||
if (value) free(node->id);
|
||||
node->id = _copyId(value);
|
||||
|
@ -2146,7 +2320,8 @@ static constexpr struct
|
|||
{"polyline", sizeof("polyline"), _createPolylineNode},
|
||||
{"line", sizeof("line"), _createLineNode},
|
||||
{"image", sizeof("image"), _createImageNode},
|
||||
{"text", sizeof("text"), _createTextNode}
|
||||
{"text", sizeof("text"), _createTextNode},
|
||||
{"feGaussianBlur", sizeof("feGaussianBlur"), _createGaussianBlurNode}
|
||||
};
|
||||
|
||||
|
||||
|
@ -2162,7 +2337,8 @@ static constexpr struct
|
|||
{"mask", sizeof("mask"), _createMaskNode},
|
||||
{"clipPath", sizeof("clipPath"), _createClipPathNode},
|
||||
{"style", sizeof("style"), _createCssStyleNode},
|
||||
{"symbol", sizeof("symbol"), _createSymbolNode}
|
||||
{"symbol", sizeof("symbol"), _createSymbolNode},
|
||||
{"filter", sizeof("filter"), _createFilterNode}
|
||||
};
|
||||
|
||||
|
||||
|
@ -3084,6 +3260,10 @@ static void _copyAttr(SvgNode* to, const SvgNode* from)
|
|||
free(to->style->mask.url);
|
||||
to->style->mask.url = strdup(from->style->mask.url);
|
||||
}
|
||||
if (from->style->filter.url) {
|
||||
if (to->style->filter.url) free(to->style->filter.url);
|
||||
to->style->filter.url = strdup(from->style->filter.url);
|
||||
}
|
||||
|
||||
//Copy node attribute
|
||||
switch (from->type) {
|
||||
|
@ -3554,13 +3734,29 @@ static void _updateComposite(SvgNode* node, SvgNode* root)
|
|||
}
|
||||
|
||||
|
||||
static void _updateFilter(SvgNode* node, SvgNode* root)
|
||||
{
|
||||
if (node->style->filter.url && !node->style->filter.node) {
|
||||
SvgNode* findResult = _findNodeById(root, node->style->filter.url);
|
||||
if (findResult) node->style->filter.node = findResult;
|
||||
}
|
||||
if (node->child.count > 0) {
|
||||
auto child = node->child.data;
|
||||
for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
|
||||
_updateFilter(*child, root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void _freeNodeStyle(SvgStyleProperty* style)
|
||||
{
|
||||
if (!style) return;
|
||||
|
||||
//style->clipPath.node and style->mask.node has only the addresses of node. Therefore, node is released from _freeNode.
|
||||
//style->clipPath.node/mask.node/filter.node has only the addresses of node. Therefore, node is released from _freeNode.
|
||||
free(style->clipPath.url);
|
||||
free(style->mask.url);
|
||||
free(style->filter.url);
|
||||
free(style->cssClass);
|
||||
|
||||
if (style->fill.paint.gradient) {
|
||||
|
@ -3761,6 +3957,9 @@ void SvgLoader::run(unsigned tid)
|
|||
_updateComposite(loaderData.doc, loaderData.doc);
|
||||
if (defs) _updateComposite(loaderData.doc, defs);
|
||||
|
||||
_updateFilter(loaderData.doc, loaderData.doc);
|
||||
if (defs) _updateFilter(loaderData.doc, defs);
|
||||
|
||||
_updateStyle(loaderData.doc, nullptr);
|
||||
if (defs) _updateStyle(defs, nullptr);
|
||||
|
||||
|
|
|
@ -26,6 +26,27 @@
|
|||
#include "tvgCommon.h"
|
||||
#include "tvgArray.h"
|
||||
|
||||
struct Box
|
||||
{
|
||||
float x, y, w, h;
|
||||
|
||||
void intersect(const Box& box)
|
||||
{
|
||||
auto x1 = x + w;
|
||||
auto y1 = y + h;
|
||||
auto x2 = box.x + box.w;
|
||||
auto y2 = box.y + box.h;
|
||||
|
||||
x = x > box.x ? x : box.x;
|
||||
y = y > box.y ? y : box.y;
|
||||
w = (x1 < x2 ? x1 : x2) - x;
|
||||
h = (y1 < y2 ? y1 : y2) - y;
|
||||
|
||||
if (w < 0.0f) w = 0.0f;
|
||||
if (h < 0.0f) h = 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
struct SvgNode;
|
||||
struct SvgStyleGradient;
|
||||
|
||||
|
@ -54,6 +75,8 @@ enum class SvgNodeType
|
|||
Mask,
|
||||
CssStyle,
|
||||
Symbol,
|
||||
Filter,
|
||||
GaussianBlur,
|
||||
Unknown
|
||||
};
|
||||
|
||||
|
@ -142,6 +165,7 @@ enum class SvgStyleFlags
|
|||
PaintOrder = 0x10000,
|
||||
StrokeMiterlimit = 0x20000,
|
||||
StrokeDashOffset = 0x40000,
|
||||
Filter = 0x80000
|
||||
};
|
||||
|
||||
constexpr bool operator &(SvgStyleFlags a, SvgStyleFlags b)
|
||||
|
@ -363,6 +387,23 @@ struct SvgTextNode
|
|||
float fontSize;
|
||||
};
|
||||
|
||||
struct SvgGaussianBlurNode
|
||||
{
|
||||
float stdDevX, stdDevY;
|
||||
Box box;
|
||||
bool isPercentage[4];
|
||||
bool hasBox;
|
||||
bool edgeModeWrap;
|
||||
};
|
||||
|
||||
struct SvgFilterNode
|
||||
{
|
||||
Box box;
|
||||
bool isPercentage[4];
|
||||
bool filterUserSpace;
|
||||
bool primitiveUserSpace;
|
||||
};
|
||||
|
||||
struct SvgLinearGradient
|
||||
{
|
||||
float x1, y1, x2, y2;
|
||||
|
@ -456,12 +497,19 @@ struct SvgStyleStroke
|
|||
SvgDash dash;
|
||||
};
|
||||
|
||||
struct SvgFilter
|
||||
{
|
||||
char *url;
|
||||
SvgNode* node;
|
||||
};
|
||||
|
||||
struct SvgStyleProperty
|
||||
{
|
||||
SvgStyleFill fill;
|
||||
SvgStyleStroke stroke;
|
||||
SvgComposite clipPath;
|
||||
SvgComposite mask;
|
||||
SvgFilter filter;
|
||||
int opacity;
|
||||
SvgColor color;
|
||||
char* cssClass;
|
||||
|
@ -498,6 +546,8 @@ struct SvgNode
|
|||
SvgCssStyleNode cssStyle;
|
||||
SvgSymbolNode symbol;
|
||||
SvgTextNode text;
|
||||
SvgFilterNode filter;
|
||||
SvgGaussianBlurNode gaussianBlur;
|
||||
} node;
|
||||
~SvgNode();
|
||||
};
|
||||
|
@ -550,9 +600,4 @@ struct SvgLoaderData
|
|||
SvgNode* currentGraphicsNode = nullptr;
|
||||
};
|
||||
|
||||
struct Box
|
||||
{
|
||||
float x, y, w, h;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -41,7 +41,7 @@ static Scene* _sceneBuildHelper(SvgLoaderData& loaderData, const SvgNode* node,
|
|||
|
||||
static inline bool _isGroupType(SvgNodeType type)
|
||||
{
|
||||
if (type == SvgNodeType::Doc || type == SvgNodeType::G || type == SvgNodeType::Use || type == SvgNodeType::ClipPath || type == SvgNodeType::Symbol) return true;
|
||||
if (type == SvgNodeType::Doc || type == SvgNodeType::G || type == SvgNodeType::Use || type == SvgNodeType::ClipPath || type == SvgNodeType::Symbol || type == SvgNodeType::Filter) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -288,6 +288,66 @@ static Paint* _applyComposition(SvgLoaderData& loaderData, Paint* paint, const S
|
|||
}
|
||||
|
||||
|
||||
static Paint* _applyFilter(SvgLoaderData& loaderData, Paint* paint, const SvgNode* node, const Box& vBox, const string& svgPath)
|
||||
{
|
||||
auto filterNode = node->style->filter.node;
|
||||
if (!filterNode || filterNode->child.count == 0) return paint;
|
||||
auto& filter = filterNode->node.filter;
|
||||
|
||||
auto scene = Scene::gen();
|
||||
|
||||
Box bbox{};
|
||||
paint->bounds(&bbox.x, &bbox.y, &bbox.w, &bbox.h, false);
|
||||
Box clipBox = filter.filterUserSpace ? filter.box : Box{bbox.x + filter.box.x * bbox.w, bbox.y + filter.box.y * bbox.h, filter.box.w * bbox.w, filter.box.h * bbox.h};
|
||||
auto primitiveUserSpace = filter.primitiveUserSpace;
|
||||
auto sx = paint->transform().e11;
|
||||
auto sy = paint->transform().e22;
|
||||
|
||||
auto child = filterNode->child.data;
|
||||
for (uint32_t i = 0; i < filterNode->child.count; ++i, ++child) {
|
||||
if ((*child)->type == SvgNodeType::GaussianBlur) {
|
||||
auto& gauss = (*child)->node.gaussianBlur;
|
||||
|
||||
auto direction = gauss.stdDevX > 0.0f ? (gauss.stdDevY > 0.0f ? 0 : 1) : (gauss.stdDevY > 0.0f ? 2 : -1);
|
||||
if (direction == -1) continue;
|
||||
|
||||
auto stdDevX = gauss.stdDevX;
|
||||
auto stdDevY = gauss.stdDevY;
|
||||
if (gauss.hasBox) {
|
||||
auto gaussBox = gauss.box;
|
||||
auto isPercent = gauss.isPercentage;
|
||||
if (primitiveUserSpace) {
|
||||
if (isPercent[0]) gaussBox.x *= loaderData.svgParse->global.w;
|
||||
if (isPercent[1]) gaussBox.y *= loaderData.svgParse->global.h;
|
||||
if (isPercent[2]) gaussBox.w *= loaderData.svgParse->global.w;
|
||||
if (isPercent[3]) gaussBox.h *= loaderData.svgParse->global.h;
|
||||
} else {
|
||||
stdDevX *= bbox.w;
|
||||
stdDevY *= bbox.h;
|
||||
if (isPercent[0]) gaussBox.x = bbox.x + gauss.box.x * bbox.w;
|
||||
if (isPercent[1]) gaussBox.y = bbox.y + gauss.box.y * bbox.h;
|
||||
if (isPercent[2]) gaussBox.w *= bbox.w;
|
||||
if (isPercent[3]) gaussBox.h *= bbox.h;
|
||||
}
|
||||
clipBox.intersect(gaussBox);
|
||||
} else if (!primitiveUserSpace) {
|
||||
stdDevX *= bbox.w;
|
||||
stdDevY *= bbox.h;
|
||||
}
|
||||
scene->push(SceneEffect::GaussianBlur, 1.25f * (direction == 2 ? stdDevY * sy : stdDevX * sx), direction, gauss.edgeModeWrap, 55);
|
||||
}
|
||||
}
|
||||
|
||||
scene->push(paint);
|
||||
|
||||
auto clip = Shape::gen();
|
||||
clip->appendRect(clipBox.x, clipBox.y, clipBox.w, clipBox.h);
|
||||
clip->transform(paint->transform());
|
||||
scene->clip(clip);
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
||||
static Paint* _applyProperty(SvgLoaderData& loaderData, SvgNode* node, Shape* vg, const Box& vBox, const string& svgPath, bool clip)
|
||||
{
|
||||
SvgStyleProperty* style = node->style;
|
||||
|
@ -352,7 +412,8 @@ static Paint* _applyProperty(SvgLoaderData& loaderData, SvgNode* node, Shape* vg
|
|||
vg->strokeFill(style->stroke.paint.color.r, style->stroke.paint.color.g, style->stroke.paint.color.b, style->stroke.opacity);
|
||||
}
|
||||
|
||||
return _applyComposition(loaderData, vg, node, vBox, svgPath);
|
||||
auto p = _applyFilter(loaderData, vg, node, vBox, svgPath);
|
||||
return _applyComposition(loaderData, p, node, vBox, svgPath);
|
||||
}
|
||||
|
||||
|
||||
|
@ -596,7 +657,8 @@ static Paint* _imageBuildHelper(SvgLoaderData& loaderData, SvgNode* node, const
|
|||
if (node->transform) m = *node->transform * m;
|
||||
picture->transform(m);
|
||||
|
||||
return _applyComposition(loaderData, picture, node, vBox, svgPath);
|
||||
auto p = _applyFilter(loaderData, picture, node, vBox, svgPath);
|
||||
return _applyComposition(loaderData, p, node, vBox, svgPath);
|
||||
}
|
||||
|
||||
|
||||
|
@ -782,7 +844,8 @@ static Paint* _textBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, c
|
|||
|
||||
_applyTextFill(node->style, text, vBox);
|
||||
|
||||
return _applyComposition(loaderData, text, node, vBox, svgPath);
|
||||
auto p = _applyFilter(loaderData, text, node, vBox, svgPath);
|
||||
return _applyComposition(loaderData, p, node, vBox, svgPath);
|
||||
}
|
||||
|
||||
|
||||
|
@ -823,7 +886,9 @@ static Scene* _sceneBuildHelper(SvgLoaderData& loaderData, const SvgNode* node,
|
|||
}
|
||||
}
|
||||
scene->opacity(node->style->opacity);
|
||||
return static_cast<Scene*>(_applyComposition(loaderData, scene, node, vBox, svgPath));
|
||||
|
||||
auto p = _applyFilter(loaderData, scene, node, vBox, svgPath);
|
||||
return static_cast<Scene*>(_applyComposition(loaderData, p, node, vBox, svgPath));
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue