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:
JunsuChoi 2020-09-24 11:52:26 +09:00 committed by Hermet Park
parent 31725b8d33
commit ea0ce1f496
3 changed files with 137 additions and 11 deletions

View file

@ -850,6 +850,14 @@ static void _handleTransformAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node
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)
{
@ -889,6 +897,7 @@ static constexpr struct
STYLE_DEF(stroke-opacity, StrokeOpacity),
STYLE_DEF(stroke-dasharray, StrokeDashArray),
STYLE_DEF(transform, Transform),
STYLE_DEF(clip-path, ClipPath),
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)
{
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)
{
loader->svgParse->node = _createNode(parent, SvgNodeType::Unknown);
loader->svgParse->node = _createNode(parent, SvgNodeType::ClipPath);
loader->svgParse->node->display = false;
simpleXmlParseAttributes(buf, bufLength, _attrParseClipPathNode, loader);
return loader->svgParse->node;
}
@ -1397,6 +1428,20 @@ static SvgNode* _findChildById(SvgNode* node, const char* id)
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)
{
@ -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)
{
if (!grad) return;
@ -2362,6 +2421,9 @@ void SvgLoader::run()
if (defs) _updateGradient(loaderData.doc, &defs->node.defs.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);
};

View file

@ -45,6 +45,7 @@ enum class SvgNodeType
Tspan,
Use,
Video,
ClipPath,
//Custome_command, //Not support
Unknown
};
@ -60,12 +61,18 @@ enum class SvgLengthType
In,
};
enum class SvgCompositeFlags
{
ClipPath = 0x01,
};
enum class SvgFillFlags
{
Paint = 0x1,
Opacity = 0x2,
Gradient = 0x4,
FillRule = 0x8
Paint = 0x01,
Opacity = 0x02,
Gradient = 0x04,
FillRule = 0x08,
ClipPath = 0x16
};
enum class SvgStrokeFlags
@ -244,6 +251,13 @@ struct SvgGradientStop
uint8_t a;
};
struct SvgComposite
{
SvgCompositeFlags flags;
string *url;
SvgNode* node;
};
struct SvgPaint
{
SvgStyleGradient* gradient;
@ -300,6 +314,7 @@ struct SvgStyleProperty
{
SvgStyleFill fill;
SvgStyleStroke stroke;
SvgComposite comp;
int opacity;
uint8_t r;
uint8_t g;

View file

@ -23,6 +23,14 @@
#include "tvgSvgSceneBuilder.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)
{
Fill::ColorStop* stops;
@ -185,6 +193,14 @@ unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient* g, Sha
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)
{
@ -252,12 +268,31 @@ void _applyProperty(SvgNode* node, Shape* vg, float vx, float vy, float vw, floa
vg->strokeColor(&r, &g, &b, &a);
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)
{
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) {
case SvgNodeType::Path: {
if (node->node.path.path) {
@ -304,26 +339,40 @@ unique_ptr<Shape> _shapeBuildHelper(SvgNode* node, float vx, float vy, float vw,
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)
{
if (node->type == SvgNodeType::Doc || node->type == SvgNodeType::G) {
if (_isGroupType(node->type)) {
auto scene = Scene::gen();
if (node->transform) scene->transform(*node->transform);
node->style->opacity = (node->style->opacity * parentOpacity) / 255.0f;
if (node->display) {
auto child = node->child.list;
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));
} else {
(*child)->style->opacity = ((*child)->style->opacity * node->style->opacity) / 255.0f;
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;