Skip to content

Commit

Permalink
SVG Export: initial support for masking
Browse files Browse the repository at this point in the history
If group is layer use first child item with blend mode 'DstIn' as mask.

Ref: #226
  • Loading branch information
rodlie committed Aug 19, 2024
1 parent 96791f9 commit ce7717d
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 23 deletions.
21 changes: 16 additions & 5 deletions src/core/Boxes/boundingbox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1399,24 +1399,35 @@ void BoundingBox::renderDataFinished(BoxRenderData *renderData) {
// }
//}

eTask* BoundingBox::saveSVGWithTransform(SvgExporter& exp, QDomElement& parent,
const FrameRange& parentVisRange) const {
eTask* BoundingBox::saveSVGWithTransform(SvgExporter& exp,
QDomElement& parent,
const FrameRange& parentVisRange,
const QString &maskId) const
{
const auto visRange = parentVisRange*prp_absInfluenceRange();
const auto task = enve::make_shared<DomEleTask>(exp, visRange);
exp.addNextTask(task);
const auto taskPtr = task.get();
const QPointer<const BoundingBox> ptr = this;
const auto expPtr = &exp;
const auto parentPtr = &parent;
taskPtr->addDependent({[ptr, taskPtr, expPtr, parentPtr, visRange]() {
taskPtr->addDependent({[ptr, taskPtr, expPtr, parentPtr, visRange, maskId]() {
auto& ele = taskPtr->element();
if(ptr) {
if (ptr) {
SvgExportHelpers::assignVisibility(*expPtr, ele, visRange);
const auto transform = ptr->mTransformAnimator.get();
const auto transformed = transform->saveSVG(*expPtr, visRange, ele);
const auto effects = ptr->mRasterEffectsAnimators.get();
const auto withEffects = effects->saveEffectsSVG(*expPtr, visRange, transformed);
parentPtr->appendChild(withEffects);

if (maskId == ptr->prp_getName()) { // move mask to defs
auto& eleMask = taskPtr->initialize("mask");
eleMask.setAttribute("id", ptr->prp_getName());
eleMask.appendChild(withEffects);
expPtr->addToDefs(eleMask);
} else {
parentPtr->appendChild(withEffects);
}
}
}, nullptr});
saveSVG(exp, taskPtr);
Expand Down
3 changes: 2 additions & 1 deletion src/core/Boxes/boundingbox.h
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,8 @@ class CORE_EXPORT BoundingBox : public eBoxOrSound {
ContainerBox* getFirstParentLayer() const;

eTask* saveSVGWithTransform(SvgExporter& exp, QDomElement& parent,
const FrameRange& parentVisRange) const;
const FrameRange& parentVisRange,
const QString &maskId = QString()) const;
private:
void cancelWaitingTasks();
void afterTotalTransformChanged(const UpdateReason reason);
Expand Down
55 changes: 38 additions & 17 deletions src/core/Boxes/containerbox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,36 +177,57 @@ OutlineSettingsAnimator *ContainerBox::getStrokeSettings() const {
return mContainedBoxes.last()->getStrokeSettings();
}

class GroupSaverSVG : public ComplexTask {
class GroupSaverSVG : public ComplexTask
{
public:
GroupSaverSVG(const ContainerBox* const src, SvgExporter& exp,
QDomElement& ele, const FrameRange& visRange) :
ComplexTask(src->getContainedBoxesCount(),
"SVG " + src->prp_getName()),
mSrc(src), mExp(exp), mEle(ele), mVisRange(visRange) {}
GroupSaverSVG(const ContainerBox* const src,
SvgExporter& exp,
QDomElement& ele,
const FrameRange& visRange)
: ComplexTask(src->getContainedBoxesCount(), "SVG " + src->prp_getName())
, mSrc(src)
, mExp(exp)
, mEle(ele)
, mVisRange(visRange)
{
// check for mask (DstIn)
if (mSrc->isLayer()) {
const auto& boxes = mSrc->getContainedBoxes();
for (const auto &box : boxes) {
if (box->getBlendMode() == SkBlendMode::kDstIn) {
mItemMaskId = box->prp_getName();
break;
}
}
if (!mItemMaskId.isEmpty()) {
mEle.setAttribute("mask", QString("url(#%1)").arg(mItemMaskId));
}
}
}

void nextStep() override {
if(!mSrc) return cancel();
if(setValue(mI)) return;
if(done()) return;
if (!mSrc) { return cancel(); }
if (setValue(mI)) { return; }
if (done()) { return; }

const auto& boxes = mSrc->getContainedBoxes();
const int id = boxes.count() - ++mI;
if(id >= boxes.count()) return finish();
if (id >= boxes.count()) { return finish(); }
const auto& box = boxes.at(id);
if(!box->isVisible()) return nextStep();
const auto task = box->saveSVGWithTransform(mExp, mEle, mVisRange);
if(task) {
addTask(task->ref<eTask>());
} else {
addEmptyTask();
}
if (!box->isVisible()) { return nextStep(); }
const auto task = box->saveSVGWithTransform(mExp,
mEle,
mVisRange,
mItemMaskId);
if (task) { addTask(task->ref<eTask>()); }
else { addEmptyTask(); }
}
private:
const QPointer<const ContainerBox> mSrc;
SvgExporter& mExp;
QDomElement& mEle;
const FrameRange mVisRange;
QString mItemMaskId;

int mI = 0;
};
Expand Down

0 comments on commit ce7717d

Please sign in to comment.