mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 05:33:36 +00:00
svg: wrapping a paint with a scene
Apply scene when a clipper or masking was required. This is a prerequisite task for migrating the clip()/mask() features from Paint to Scene.
This commit is contained in:
parent
765f927dd0
commit
c14382f3e3
1 changed files with 69 additions and 80 deletions
|
@ -223,78 +223,80 @@ static Matrix _compositionTransform(Paint* paint, const SvgNode* node, const Svg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void _applyComposition(SvgLoaderData& loaderData, Paint* paint, const SvgNode* node, const Box& vBox, const string& svgPath)
|
static Paint* _applyComposition(SvgLoaderData& loaderData, Paint* paint, const SvgNode* node, const Box& vBox, const string& svgPath)
|
||||||
{
|
{
|
||||||
/* ClipPath */
|
|
||||||
/* Do not drop in Circular Dependency for ClipPath.
|
/* Do not drop in Circular Dependency for ClipPath.
|
||||||
Composition can be applied recursively if its children nodes have composition target to this one. */
|
Composition can be applied recursively if its children nodes have composition target to this one. */
|
||||||
if (node->style->clipPath.applying) {
|
if (node->style->clipPath.applying || node->style->mask.applying) {
|
||||||
TVGLOG("SVG", "Multiple Composition Tried! Check out Circular dependency?");
|
TVGLOG("SVG", "Multiple composition tried! Check out circular dependency?");
|
||||||
} else {
|
return paint;
|
||||||
auto compNode = node->style->clipPath.node;
|
}
|
||||||
if (compNode && compNode->child.count > 0) {
|
|
||||||
node->style->clipPath.applying = true;
|
|
||||||
|
|
||||||
auto comp = Shape::gen();
|
auto clipNode = node->style->clipPath.node;
|
||||||
auto child = compNode->child.data;
|
auto maskNode = node->style->mask.node;
|
||||||
auto valid = false; //Composite only when valid shapes exist
|
auto validClip = (clipNode && clipNode->child.count > 0) ? true : false;
|
||||||
|
auto validMask = (maskNode && maskNode->child.count > 0) ? true : false;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < compNode->child.count; ++i, ++child) {
|
if (!validClip && !validMask) return paint;
|
||||||
if (_appendClipChild(loaderData, *child, comp.get(), vBox, svgPath, compNode->child.count > 1)) valid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (valid) {
|
Scene* scene = Scene::gen().release();
|
||||||
Matrix finalTransform = _compositionTransform(paint, node, compNode, SvgNodeType::ClipPath);
|
scene->push(tvg::cast(paint));
|
||||||
comp->transform(finalTransform);
|
|
||||||
paint->clip(std::move(comp));
|
|
||||||
}
|
|
||||||
|
|
||||||
node->style->clipPath.applying = false;
|
if (validClip) {
|
||||||
|
node->style->clipPath.applying = true;
|
||||||
|
|
||||||
|
auto clipper = Shape::gen();
|
||||||
|
auto child = clipNode->child.data;
|
||||||
|
auto valid = false; //Composite only when valid shapes exist
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < clipNode->child.count; ++i, ++child) {
|
||||||
|
if (_appendClipChild(loaderData, *child, clipper.get(), vBox, svgPath, clipNode->child.count > 1)) valid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (valid) {
|
||||||
|
Matrix finalTransform = _compositionTransform(paint, node, clipNode, SvgNodeType::ClipPath);
|
||||||
|
clipper->transform(finalTransform);
|
||||||
|
scene->clip(std::move(clipper));
|
||||||
|
}
|
||||||
|
|
||||||
|
node->style->clipPath.applying = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mask */
|
/* Mask */
|
||||||
/* Do not drop in Circular Dependency for Mask.
|
if (validMask) {
|
||||||
Composition can be applied recursively if its children nodes have composition target to this one. */
|
node->style->mask.applying = true;
|
||||||
if (node->style->mask.applying) {
|
|
||||||
TVGLOG("SVG", "Multiple Composition Tried! Check out Circular dependency?");
|
|
||||||
} else {
|
|
||||||
auto compNode = node->style->mask.node;
|
|
||||||
if (compNode && compNode->child.count > 0) {
|
|
||||||
node->style->mask.applying = true;
|
|
||||||
|
|
||||||
if (auto comp = _sceneBuildHelper(loaderData, compNode, vBox, svgPath, true, 0)) {
|
if (auto mask = _sceneBuildHelper(loaderData, maskNode, vBox, svgPath, true, 0)) {
|
||||||
if (!compNode->node.mask.userSpace) {
|
if (!maskNode->node.mask.userSpace) {
|
||||||
Matrix finalTransform = _compositionTransform(paint, node, compNode, SvgNodeType::Mask);
|
Matrix finalTransform = _compositionTransform(paint, node, maskNode, SvgNodeType::Mask);
|
||||||
comp->transform(finalTransform);
|
mask->transform(finalTransform);
|
||||||
} else if (node->transform) {
|
} else if (node->transform) {
|
||||||
comp->transform(*node->transform);
|
mask->transform(*node->transform);
|
||||||
}
|
|
||||||
if (compNode->node.mask.type == SvgMaskType::Luminance) paint->mask(std::move(comp), MaskMethod::Luma);
|
|
||||||
else paint->mask(std::move(comp), MaskMethod::Alpha);
|
|
||||||
}
|
}
|
||||||
|
scene->mask(std::move(mask), maskNode->node.mask.type == SvgMaskType::Luminance ? MaskMethod::Luma: MaskMethod::Alpha);
|
||||||
node->style->mask.applying = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node->style->mask.applying = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void _applyProperty(SvgLoaderData& loaderData, SvgNode* node, Shape* vg, const Box& vBox, const string& svgPath, bool clip)
|
static Paint* _applyProperty(SvgLoaderData& loaderData, SvgNode* node, Shape* vg, const Box& vBox, const string& svgPath, bool clip)
|
||||||
{
|
{
|
||||||
SvgStyleProperty* style = node->style;
|
SvgStyleProperty* style = node->style;
|
||||||
|
|
||||||
//Clip transformation is applied directly to the path in the _appendClipShape function
|
//Clip transformation is applied directly to the path in the _appendClipShape function
|
||||||
if (node->transform && !clip) vg->transform(*node->transform);
|
if (node->transform && !clip) vg->transform(*node->transform);
|
||||||
if (node->type == SvgNodeType::Doc || !node->style->display) return;
|
if (node->type == SvgNodeType::Doc || !node->style->display) return vg;
|
||||||
|
|
||||||
//If fill property is nullptr then do nothing
|
//If fill property is nullptr then do nothing
|
||||||
if (style->fill.paint.none) {
|
if (style->fill.paint.none) {
|
||||||
//Do nothing
|
//Do nothing
|
||||||
} else if (style->fill.paint.gradient) {
|
} else if (style->fill.paint.gradient) {
|
||||||
Box bBox = vBox;
|
auto bBox = vBox;
|
||||||
if (!style->fill.paint.gradient->userSpace) bBox = _boundingBox(vg);
|
if (!style->fill.paint.gradient->userSpace) bBox = _boundingBox(vg);
|
||||||
|
|
||||||
if (style->fill.paint.gradient->type == SvgGradientType::Linear) {
|
if (style->fill.paint.gradient->type == SvgGradientType::Linear) {
|
||||||
auto linear = _applyLinearGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity);
|
auto linear = _applyLinearGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity);
|
||||||
vg->fill(std::move(linear));
|
vg->fill(std::move(linear));
|
||||||
|
@ -303,7 +305,6 @@ static void _applyProperty(SvgLoaderData& loaderData, SvgNode* node, Shape* vg,
|
||||||
vg->fill(std::move(radial));
|
vg->fill(std::move(radial));
|
||||||
}
|
}
|
||||||
} else if (style->fill.paint.url) {
|
} else if (style->fill.paint.url) {
|
||||||
//TODO: Apply the color pointed by url
|
|
||||||
TVGLOG("SVG", "The fill's url not supported.");
|
TVGLOG("SVG", "The fill's url not supported.");
|
||||||
} else if (style->fill.paint.curColor) {
|
} else if (style->fill.paint.curColor) {
|
||||||
//Apply the current style color
|
//Apply the current style color
|
||||||
|
@ -317,24 +318,21 @@ static void _applyProperty(SvgLoaderData& loaderData, SvgNode* node, Shape* vg,
|
||||||
vg->order(!style->paintOrder);
|
vg->order(!style->paintOrder);
|
||||||
vg->opacity(style->opacity);
|
vg->opacity(style->opacity);
|
||||||
|
|
||||||
if (node->type == SvgNodeType::G || node->type == SvgNodeType::Use) return;
|
if (node->type == SvgNodeType::G || node->type == SvgNodeType::Use) return vg;
|
||||||
|
|
||||||
//Apply the stroke style property
|
//Apply the stroke style property
|
||||||
vg->strokeWidth(style->stroke.width);
|
vg->strokeWidth(style->stroke.width);
|
||||||
vg->strokeCap(style->stroke.cap);
|
vg->strokeCap(style->stroke.cap);
|
||||||
vg->strokeJoin(style->stroke.join);
|
vg->strokeJoin(style->stroke.join);
|
||||||
vg->strokeMiterlimit(style->stroke.miterlimit);
|
vg->strokeMiterlimit(style->stroke.miterlimit);
|
||||||
if (style->stroke.dash.array.count > 0) {
|
vg->strokeDash(style->stroke.dash.array.data, style->stroke.dash.array.count, style->stroke.dash.offset);
|
||||||
vg->strokeDash(style->stroke.dash.array.data, style->stroke.dash.array.count, style->stroke.dash.offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
//If stroke property is nullptr then do nothing
|
//If stroke property is nullptr then do nothing
|
||||||
if (style->stroke.paint.none) {
|
if (style->stroke.paint.none) {
|
||||||
vg->strokeWidth(0.0f);
|
vg->strokeWidth(0.0f);
|
||||||
} else if (style->stroke.paint.gradient) {
|
} else if (style->stroke.paint.gradient) {
|
||||||
Box bBox = vBox;
|
auto bBox = vBox;
|
||||||
if (!style->stroke.paint.gradient->userSpace) bBox = _boundingBox(vg);
|
if (!style->stroke.paint.gradient->userSpace) bBox = _boundingBox(vg);
|
||||||
|
|
||||||
if (style->stroke.paint.gradient->type == SvgGradientType::Linear) {
|
if (style->stroke.paint.gradient->type == SvgGradientType::Linear) {
|
||||||
auto linear = _applyLinearGradientProperty(style->stroke.paint.gradient, bBox, style->stroke.opacity);
|
auto linear = _applyLinearGradientProperty(style->stroke.paint.gradient, bBox, style->stroke.opacity);
|
||||||
vg->strokeFill(std::move(linear));
|
vg->strokeFill(std::move(linear));
|
||||||
|
@ -353,7 +351,7 @@ static void _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);
|
vg->strokeFill(style->stroke.paint.color.r, style->stroke.paint.color.g, style->stroke.paint.color.b, style->stroke.opacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
_applyComposition(loaderData, vg, node, vBox, svgPath);
|
return _applyComposition(loaderData, vg, node, vBox, svgPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -413,12 +411,11 @@ static bool _recognizeShape(SvgNode* node, Shape* shape)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static unique_ptr<Shape> _shapeBuildHelper(SvgLoaderData& loaderData, SvgNode* node, const Box& vBox, const string& svgPath)
|
static Paint* _shapeBuildHelper(SvgLoaderData& loaderData, SvgNode* node, const Box& vBox, const string& svgPath)
|
||||||
{
|
{
|
||||||
auto shape = Shape::gen();
|
auto shape = Shape::gen();
|
||||||
if (!_recognizeShape(node, shape.get())) return nullptr;
|
if (!_recognizeShape(node, shape.get())) return nullptr;
|
||||||
_applyProperty(loaderData, node, shape.get(), vBox, svgPath, false);
|
return _applyProperty(loaderData, node, shape.release(), vBox, svgPath, false);
|
||||||
return shape;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -521,9 +518,10 @@ static bool _isValidImageMimeTypeAndEncoding(const char** href, const char** mim
|
||||||
|
|
||||||
#include "tvgTaskScheduler.h"
|
#include "tvgTaskScheduler.h"
|
||||||
|
|
||||||
static unique_ptr<Picture> _imageBuildHelper(SvgLoaderData& loaderData, SvgNode* node, const Box& vBox, const string& svgPath)
|
static Paint* _imageBuildHelper(SvgLoaderData& loaderData, SvgNode* node, const Box& vBox, const string& svgPath)
|
||||||
{
|
{
|
||||||
if (!node->node.image.href || !strlen(node->node.image.href)) return nullptr;
|
if (!node->node.image.href || !strlen(node->node.image.href)) return nullptr;
|
||||||
|
|
||||||
auto picture = Picture::gen();
|
auto picture = Picture::gen();
|
||||||
|
|
||||||
TaskScheduler::async(false); //force to load a picture on the same thread
|
TaskScheduler::async(false); //force to load a picture on the same thread
|
||||||
|
@ -586,9 +584,7 @@ static unique_ptr<Picture> _imageBuildHelper(SvgLoaderData& loaderData, SvgNode*
|
||||||
if (node->transform) m = *node->transform * m;
|
if (node->transform) m = *node->transform * m;
|
||||||
picture->transform(m);
|
picture->transform(m);
|
||||||
|
|
||||||
_applyComposition(loaderData, picture.get(), node, vBox, svgPath);
|
return _applyComposition(loaderData, picture.release(), node, vBox, svgPath);
|
||||||
|
|
||||||
return picture;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -754,15 +750,16 @@ static void _applyTextFill(SvgStyleProperty* style, Text* text, const Box& vBox)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static unique_ptr<Text> _textBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath)
|
static Paint* _textBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath)
|
||||||
{
|
{
|
||||||
auto textNode = &node->node.text;
|
auto textNode = &node->node.text;
|
||||||
if (!textNode->text) return nullptr;
|
if (!textNode->text) return nullptr;
|
||||||
auto text = Text::gen();
|
|
||||||
|
auto text = Text::gen().release();
|
||||||
|
|
||||||
Matrix textTransform;
|
Matrix textTransform;
|
||||||
if (node->transform) textTransform = *node->transform;
|
if (node->transform) textTransform = *node->transform;
|
||||||
else textTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
|
else textTransform = {1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f};
|
||||||
|
|
||||||
translateR(&textTransform, node->node.text.x, node->node.text.y - textNode->fontSize);
|
translateR(&textTransform, node->node.text.x, node->node.text.y - textNode->fontSize);
|
||||||
text->transform(textTransform);
|
text->transform(textTransform);
|
||||||
|
@ -773,10 +770,9 @@ static unique_ptr<Text> _textBuildHelper(SvgLoaderData& loaderData, const SvgNod
|
||||||
if (textNode->fontFamily) text->font(textNode->fontFamily, fontSizePt);
|
if (textNode->fontFamily) text->font(textNode->fontFamily, fontSizePt);
|
||||||
text->text(textNode->text);
|
text->text(textNode->text);
|
||||||
|
|
||||||
_applyTextFill(node->style, text.get(), vBox);
|
_applyTextFill(node->style, text, vBox);
|
||||||
_applyComposition(loaderData, text.get(), node, vBox, svgPath);
|
|
||||||
|
|
||||||
return text;
|
return _applyComposition(loaderData, text, node, vBox, svgPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -805,26 +801,19 @@ static unique_ptr<Scene> _sceneBuildHelper(SvgLoaderData& loaderData, const SvgN
|
||||||
else if (!((*child)->type == SvgNodeType::Symbol && node->type != SvgNodeType::Use))
|
else if (!((*child)->type == SvgNodeType::Symbol && node->type != SvgNodeType::Use))
|
||||||
scene->push(_sceneBuildHelper(loaderData, *child, vBox, svgPath, false, depth + 1));
|
scene->push(_sceneBuildHelper(loaderData, *child, vBox, svgPath, false, depth + 1));
|
||||||
if ((*child)->id) scene->id = djb2Encode((*child)->id);
|
if ((*child)->id) scene->id = djb2Encode((*child)->id);
|
||||||
} else if ((*child)->type == SvgNodeType::Image) {
|
} else {
|
||||||
if (auto image = _imageBuildHelper(loaderData, *child, vBox, svgPath)) {
|
Paint* paint = nullptr;
|
||||||
if ((*child)->id) image->id = djb2Encode((*child)->id);
|
if ((*child)->type == SvgNodeType::Image) paint = _imageBuildHelper(loaderData, *child, vBox, svgPath);
|
||||||
scene->push(std::move(image));
|
else if ((*child)->type == SvgNodeType::Text) paint = _textBuildHelper(loaderData, *child, vBox, svgPath);
|
||||||
}
|
else if ((*child)->type != SvgNodeType::Mask) paint = _shapeBuildHelper(loaderData, *child, vBox, svgPath);
|
||||||
} else if ((*child)->type == SvgNodeType::Text) {
|
if (paint) {
|
||||||
if (auto text = _textBuildHelper(loaderData, *child, vBox, svgPath)) {
|
if ((*child)->id) paint->id = djb2Encode((*child)->id);
|
||||||
if ((*child)->id) text->id = djb2Encode((*child)->id);
|
scene->push(tvg::cast(paint));
|
||||||
scene->push(std::move(text));
|
|
||||||
}
|
|
||||||
} else if ((*child)->type != SvgNodeType::Mask) {
|
|
||||||
if (auto shape = _shapeBuildHelper(loaderData, *child, vBox, svgPath)) {
|
|
||||||
if ((*child)->id) shape->id = djb2Encode((*child)->id);
|
|
||||||
scene->push(std::move(shape));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_applyComposition(loaderData, scene.get(), node, vBox, svgPath);
|
|
||||||
scene->opacity(node->style->opacity);
|
scene->opacity(node->style->opacity);
|
||||||
return scene;
|
return tvg::cast<Scene>(_applyComposition(loaderData, scene.release(), node, vBox, svgPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue