mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-14 12:04:29 +00:00
SvgLoader: Implement ClipPath style
Supports case of using style attribute for defined <clipPath>. In SVG, <clipPath> can be used as a "clipPath" attribute or a style "clip-path". This patch only supports "clip-path" of style is declared. The remaining features will be added later. [Example SVG case] <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve"> <defs> <clipPath id="clipPath"> <rect x="15" y="15" width="40" height="40" fill="#F00" /> <circle cx="20" cy="20" r="10" fill="#F00" /> </clipPath> </defs> <circle cx="25" cy="25" r="20" style="fill: #0000ff; clip-path: url(#clipPath); " /> </svg>
This commit is contained in:
parent
31725b8d33
commit
ea0ce1f496
3 changed files with 137 additions and 11 deletions
|
@ -850,6 +850,14 @@ static void _handleTransformAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node
|
||||||
node->transform = _parseTransformationMatrix(value);
|
node->transform = _parseTransformationMatrix(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _handleClipPathAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
|
||||||
|
{
|
||||||
|
SvgStyleProperty* style = node->style;
|
||||||
|
style->comp.flags = (SvgCompositeFlags)((int)style->comp.flags | (int)SvgCompositeFlags::ClipPath);
|
||||||
|
|
||||||
|
int len = strlen(value);
|
||||||
|
if (len >= 3 && !strncmp(value, "url", 3)) style->comp.url = _idFromUrl((const char*)(value + 3));
|
||||||
|
}
|
||||||
|
|
||||||
static void _handleDisplayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
|
static void _handleDisplayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
|
||||||
{
|
{
|
||||||
|
@ -889,6 +897,7 @@ static constexpr struct
|
||||||
STYLE_DEF(stroke-opacity, StrokeOpacity),
|
STYLE_DEF(stroke-opacity, StrokeOpacity),
|
||||||
STYLE_DEF(stroke-dasharray, StrokeDashArray),
|
STYLE_DEF(stroke-dasharray, StrokeDashArray),
|
||||||
STYLE_DEF(transform, Transform),
|
STYLE_DEF(transform, Transform),
|
||||||
|
STYLE_DEF(clip-path, ClipPath),
|
||||||
STYLE_DEF(display, Display)
|
STYLE_DEF(display, Display)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -938,6 +947,26 @@ static bool _attrParseGNode(void* data, const char* key, const char* value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* parse clipPath node
|
||||||
|
* https://www.w3.org/TR/SVG/struct.html#Groups
|
||||||
|
*/
|
||||||
|
static bool _attrParseClipPathNode(void* data, const char* key, const char* value)
|
||||||
|
{
|
||||||
|
SvgLoaderData* loader = (SvgLoaderData*)data;
|
||||||
|
SvgNode* node = loader->svgParse->node;
|
||||||
|
|
||||||
|
if (!strcmp(key, "style")) {
|
||||||
|
return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
|
||||||
|
} else if (!strcmp(key, "transform")) {
|
||||||
|
node->transform = _parseTransformationMatrix(value);
|
||||||
|
} else if (!strcmp(key, "id")) {
|
||||||
|
node->id = _copyId(value);
|
||||||
|
} else {
|
||||||
|
return _parseStyleAttr(loader, key, value);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static SvgNode* _createNode(SvgNode* parent, SvgNodeType type)
|
static SvgNode* _createNode(SvgNode* parent, SvgNodeType type)
|
||||||
{
|
{
|
||||||
SvgNode* node = (SvgNode*)calloc(1, sizeof(SvgNode));
|
SvgNode* node = (SvgNode*)calloc(1, sizeof(SvgNode));
|
||||||
|
@ -1019,10 +1048,12 @@ static SvgNode* _createMaskNode(SvgLoaderData* loader, SvgNode* parent, const ch
|
||||||
|
|
||||||
static SvgNode* _createClipPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
|
static SvgNode* _createClipPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
|
||||||
{
|
{
|
||||||
loader->svgParse->node = _createNode(parent, SvgNodeType::Unknown);
|
loader->svgParse->node = _createNode(parent, SvgNodeType::ClipPath);
|
||||||
|
|
||||||
loader->svgParse->node->display = false;
|
loader->svgParse->node->display = false;
|
||||||
|
|
||||||
|
simpleXmlParseAttributes(buf, bufLength, _attrParseClipPathNode, loader);
|
||||||
|
|
||||||
return loader->svgParse->node;
|
return loader->svgParse->node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1397,6 +1428,20 @@ static SvgNode* _findChildById(SvgNode* node, const char* id)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SvgNode* _findNodeById(SvgNode *node, string* id)
|
||||||
|
{
|
||||||
|
SvgNode* result = nullptr;
|
||||||
|
if ((node->id != nullptr) && !node->id->compare(*id)) return node;
|
||||||
|
|
||||||
|
if (node->child.cnt > 0) {
|
||||||
|
auto child = node->child.list;
|
||||||
|
for (uint32_t i = 0; i < node->child.cnt; ++i, ++child) {
|
||||||
|
result = _findNodeById(*child, id);
|
||||||
|
if (result) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static void _cloneGradStops(SvgVector<Fill::ColorStop*>* dst, SvgVector<Fill::ColorStop*>* src)
|
static void _cloneGradStops(SvgVector<Fill::ColorStop*>* dst, SvgVector<Fill::ColorStop*>* src)
|
||||||
{
|
{
|
||||||
|
@ -2200,6 +2245,20 @@ static void _updateGradient(SvgNode* node, SvgVector<SvgStyleGradient*>* gradide
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _updateComposite(SvgNode* node, SvgNode* root)
|
||||||
|
{
|
||||||
|
if (node->style->comp.url && !node->style->comp.node) {
|
||||||
|
SvgNode *findResult = _findNodeById(root, node->style->comp.url);
|
||||||
|
if (findResult) node->style->comp.node = findResult;
|
||||||
|
}
|
||||||
|
if (node->child.cnt > 0) {
|
||||||
|
auto child = node->child.list;
|
||||||
|
for (uint32_t i = 0; i < node->child.cnt; ++i, ++child) {
|
||||||
|
_updateComposite(*child, root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void _freeGradientStyle(SvgStyleGradient* grad)
|
static void _freeGradientStyle(SvgStyleGradient* grad)
|
||||||
{
|
{
|
||||||
if (!grad) return;
|
if (!grad) return;
|
||||||
|
@ -2362,6 +2421,9 @@ void SvgLoader::run()
|
||||||
if (defs) _updateGradient(loaderData.doc, &defs->node.defs.gradients);
|
if (defs) _updateGradient(loaderData.doc, &defs->node.defs.gradients);
|
||||||
|
|
||||||
if (loaderData.gradients.cnt > 0) _updateGradient(loaderData.doc, &loaderData.gradients);
|
if (loaderData.gradients.cnt > 0) _updateGradient(loaderData.doc, &loaderData.gradients);
|
||||||
|
|
||||||
|
_updateComposite(loaderData.doc, loaderData.doc);
|
||||||
|
if (defs) _updateComposite(loaderData.doc, defs);
|
||||||
}
|
}
|
||||||
root = builder.build(loaderData.doc);
|
root = builder.build(loaderData.doc);
|
||||||
};
|
};
|
||||||
|
|
|
@ -45,6 +45,7 @@ enum class SvgNodeType
|
||||||
Tspan,
|
Tspan,
|
||||||
Use,
|
Use,
|
||||||
Video,
|
Video,
|
||||||
|
ClipPath,
|
||||||
//Custome_command, //Not support
|
//Custome_command, //Not support
|
||||||
Unknown
|
Unknown
|
||||||
};
|
};
|
||||||
|
@ -60,12 +61,18 @@ enum class SvgLengthType
|
||||||
In,
|
In,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class SvgCompositeFlags
|
||||||
|
{
|
||||||
|
ClipPath = 0x01,
|
||||||
|
};
|
||||||
|
|
||||||
enum class SvgFillFlags
|
enum class SvgFillFlags
|
||||||
{
|
{
|
||||||
Paint = 0x1,
|
Paint = 0x01,
|
||||||
Opacity = 0x2,
|
Opacity = 0x02,
|
||||||
Gradient = 0x4,
|
Gradient = 0x04,
|
||||||
FillRule = 0x8
|
FillRule = 0x08,
|
||||||
|
ClipPath = 0x16
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class SvgStrokeFlags
|
enum class SvgStrokeFlags
|
||||||
|
@ -244,6 +251,13 @@ struct SvgGradientStop
|
||||||
uint8_t a;
|
uint8_t a;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SvgComposite
|
||||||
|
{
|
||||||
|
SvgCompositeFlags flags;
|
||||||
|
string *url;
|
||||||
|
SvgNode* node;
|
||||||
|
};
|
||||||
|
|
||||||
struct SvgPaint
|
struct SvgPaint
|
||||||
{
|
{
|
||||||
SvgStyleGradient* gradient;
|
SvgStyleGradient* gradient;
|
||||||
|
@ -300,6 +314,7 @@ struct SvgStyleProperty
|
||||||
{
|
{
|
||||||
SvgStyleFill fill;
|
SvgStyleFill fill;
|
||||||
SvgStyleStroke stroke;
|
SvgStyleStroke stroke;
|
||||||
|
SvgComposite comp;
|
||||||
int opacity;
|
int opacity;
|
||||||
uint8_t r;
|
uint8_t r;
|
||||||
uint8_t g;
|
uint8_t g;
|
||||||
|
|
|
@ -23,6 +23,14 @@
|
||||||
#include "tvgSvgSceneBuilder.h"
|
#include "tvgSvgSceneBuilder.h"
|
||||||
#include "tvgSvgPath.h"
|
#include "tvgSvgPath.h"
|
||||||
|
|
||||||
|
void _appendShape(SvgNode* node, Shape* shape, float vx, float vy, float vw, float vh);
|
||||||
|
|
||||||
|
bool _isGroupType(SvgNodeType type)
|
||||||
|
{
|
||||||
|
if (type == SvgNodeType::Doc || type == SvgNodeType::G || type == SvgNodeType::ClipPath) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
unique_ptr<LinearGradient> _applyLinearGradientProperty(SvgStyleGradient* g, Shape* vg, float rx, float ry, float rw, float rh)
|
unique_ptr<LinearGradient> _applyLinearGradientProperty(SvgStyleGradient* g, Shape* vg, float rx, float ry, float rw, float rh)
|
||||||
{
|
{
|
||||||
Fill::ColorStop* stops;
|
Fill::ColorStop* stops;
|
||||||
|
@ -185,6 +193,14 @@ unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient* g, Sha
|
||||||
return fillGrad;
|
return fillGrad;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _appendChildShape(SvgNode* node, Shape* shape, float vx, float vy, float vw, float vh)
|
||||||
|
{
|
||||||
|
_appendShape(node, shape, vx, vy, vw, vh);
|
||||||
|
if (node->child.cnt > 0) {
|
||||||
|
auto child = node->child.list;
|
||||||
|
for (uint32_t i = 0; i < node->child.cnt; ++i, ++child) _appendChildShape(*child, shape, vx, vy, vw, vh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _applyProperty(SvgNode* node, Shape* vg, float vx, float vy, float vw, float vh)
|
void _applyProperty(SvgNode* node, Shape* vg, float vx, float vy, float vw, float vh)
|
||||||
{
|
{
|
||||||
|
@ -252,12 +268,31 @@ void _applyProperty(SvgNode* node, Shape* vg, float vx, float vy, float vw, floa
|
||||||
vg->strokeColor(&r, &g, &b, &a);
|
vg->strokeColor(&r, &g, &b, &a);
|
||||||
vg->stroke(r, g, b, (a * style->opacity) / 255.0f);
|
vg->stroke(r, g, b, (a * style->opacity) / 255.0f);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
//Apply composite node
|
||||||
|
if (style->comp.node) {
|
||||||
|
//Composite ClipPath
|
||||||
|
if (((int)style->comp.flags & (int)SvgCompositeFlags::ClipPath)) {
|
||||||
|
auto compNode = style->comp.node;
|
||||||
|
if (compNode->child.cnt > 0) {
|
||||||
|
auto comp = Shape::gen();
|
||||||
|
auto child = compNode->child.list;
|
||||||
|
for (uint32_t i = 0; i < compNode->child.cnt; ++i, ++child) _appendChildShape(*child, comp.get(), vx, vy, vw, vh);
|
||||||
|
vg->composite(move(comp), CompositeMethod::ClipPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unique_ptr<Shape> _shapeBuildHelper(SvgNode* node, float vx, float vy, float vw, float vh)
|
unique_ptr<Shape> _shapeBuildHelper(SvgNode* node, float vx, float vy, float vw, float vh)
|
||||||
{
|
{
|
||||||
auto shape = Shape::gen();
|
auto shape = Shape::gen();
|
||||||
|
_appendShape(node, shape.get(), vx, vy, vw, vh);
|
||||||
|
return shape;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _appendShape(SvgNode* node, Shape* shape, float vx, float vy, float vw, float vh)
|
||||||
|
{
|
||||||
switch (node->type) {
|
switch (node->type) {
|
||||||
case SvgNodeType::Path: {
|
case SvgNodeType::Path: {
|
||||||
if (node->node.path.path) {
|
if (node->node.path.path) {
|
||||||
|
@ -304,26 +339,40 @@ unique_ptr<Shape> _shapeBuildHelper(SvgNode* node, float vx, float vy, float vw,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_applyProperty(node, shape.get(), vx, vy, vw, vh);
|
|
||||||
return shape;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
_applyProperty(node, shape, vx, vy, vw, vh);
|
||||||
|
}
|
||||||
|
|
||||||
unique_ptr<Scene> _sceneBuildHelper(SvgNode* node, float vx, float vy, float vw, float vh, int parentOpacity)
|
unique_ptr<Scene> _sceneBuildHelper(SvgNode* node, float vx, float vy, float vw, float vh, int parentOpacity)
|
||||||
{
|
{
|
||||||
if (node->type == SvgNodeType::Doc || node->type == SvgNodeType::G) {
|
if (_isGroupType(node->type)) {
|
||||||
auto scene = Scene::gen();
|
auto scene = Scene::gen();
|
||||||
if (node->transform) scene->transform(*node->transform);
|
if (node->transform) scene->transform(*node->transform);
|
||||||
node->style->opacity = (node->style->opacity * parentOpacity) / 255.0f;
|
node->style->opacity = (node->style->opacity * parentOpacity) / 255.0f;
|
||||||
|
|
||||||
if (node->display) {
|
if (node->display) {
|
||||||
auto child = node->child.list;
|
auto child = node->child.list;
|
||||||
for (uint32_t i = 0; i < node->child.cnt; ++i, ++child) {
|
for (uint32_t i = 0; i < node->child.cnt; ++i, ++child) {
|
||||||
if ((*child)->type == SvgNodeType::Doc || (*child)->type == SvgNodeType::G) {
|
if (_isGroupType((*child)->type)) {
|
||||||
scene->push(_sceneBuildHelper(*child, vx, vy, vw, vh, node->style->opacity));
|
scene->push(_sceneBuildHelper(*child, vx, vy, vw, vh, node->style->opacity));
|
||||||
} else {
|
} else {
|
||||||
(*child)->style->opacity = ((*child)->style->opacity * node->style->opacity) / 255.0f;
|
(*child)->style->opacity = ((*child)->style->opacity * node->style->opacity) / 255.0f;
|
||||||
scene->push(_shapeBuildHelper(*child, vx, vy, vw, vh));
|
scene->push(_shapeBuildHelper(*child, vx, vy, vw, vh));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Apply composite node
|
||||||
|
if (node->style->comp.node) {
|
||||||
|
//Composite ClipPath
|
||||||
|
if (((int)node->style->comp.flags & (int)SvgCompositeFlags::ClipPath)) {
|
||||||
|
auto compNode = node->style->comp.node;
|
||||||
|
if (compNode->child.cnt > 0) {
|
||||||
|
auto comp = Shape::gen();
|
||||||
|
auto child = compNode->child.list;
|
||||||
|
for (uint32_t i = 0; i < compNode->child.cnt; ++i, ++child) _appendChildShape(*child, comp.get(), vx, vy, vw, vh);
|
||||||
|
scene->composite(move(comp), CompositeMethod::ClipPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return scene;
|
return scene;
|
||||||
|
|
Loading…
Add table
Reference in a new issue