Skip to content

Commit

Permalink
Inverted masks
Browse files Browse the repository at this point in the history
  • Loading branch information
mbasaglia committed Jan 21, 2022
1 parent cada666 commit 009a92f
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 51 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* Added Inflate/Deflate modifier (aka Pucker/Bloat)
* Added support for rounded polygons / stars
* Added rounded corners modifier
* Added support for inverted masks
* I/O:
* Loading Lottie with `meta` no longer show a warning
* Fixed UUIDs when loading lottie animations
Expand All @@ -17,6 +18,7 @@
* Fixed parsing SVG paths
* Added support for loading basic animated SVG paths
* Fixed importing solid layers from lottie
* Fixed importing mattes affecting precomps
* UI:
* Added simplified Chinese translation
* Added British English translation
Expand Down
2 changes: 1 addition & 1 deletion src/core/io/glaxnimate/glaxnimate_format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ using namespace glaxnimate;

io::Autoreg<io::glaxnimate::GlaxnimateFormat> io::glaxnimate::GlaxnimateFormat::autoreg;

const int glaxnimate::io::glaxnimate::GlaxnimateFormat::format_version = 5;
const int glaxnimate::io::glaxnimate::GlaxnimateFormat::format_version = 6;



Expand Down
6 changes: 6 additions & 0 deletions src/core/io/glaxnimate/import_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,12 @@ class ImportState
object["multiple"] = "Individually";
}
}

if ( document_version < 6 )
{
if ( object["__type__"].toString() == "MaskSettings" )
object["mask"] = int(object["mask"].toBool());
}
}

void do_load_object ( model::Object* target, QJsonObject object, const UnresolvedPath& path )
Expand Down
95 changes: 49 additions & 46 deletions src/core/io/lottie/lottie_importer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include "lottie_private_common.hpp"
#include "io/svg/svg_parser.hpp"

#include <QDebug>

namespace glaxnimate::io::lottie::detail {

struct FontInfo
Expand Down Expand Up @@ -123,11 +125,6 @@ class LottieImporterState
load_layer(pair.second, static_cast<model::Layer*>(pair.first));
}

bool has_mask(const QJsonObject& json)
{
return mask && json["tt"].toInt();
}

void load_visibility(model::VisualNode* node, const QJsonObject& json)
{
if ( json.contains("hd") && json["hd"].toBool() )
Expand All @@ -145,39 +142,63 @@ class LottieImporterState
}

int ty = json["ty"].toInt();

std::unique_ptr<model::ShapeElement> inner_shape;
bool start_mask = json["td"].toInt();

if ( ty == 0 )
{
load_precomp_layer(json, referenced);
mask = nullptr;
inner_shape = load_precomp_layer(json);

auto op = document->main()->animation->last_frame.get();
if ( json.contains("parent") || referenced.count(index) || json["ip"].toDouble() != 0 ||
json["op"].toDouble(op) != op || start_mask
)
{
auto layer = make_node<model::Layer>(document);
layer->name.set(inner_shape->name.get());
layer->shapes.insert(std::move(inner_shape), 0);
layer_indices[index] = layer.get();
deferred.emplace_back(layer.get(), json);
inner_shape = std::move(layer);
}
}
else
{
auto layer = std::make_unique<model::Layer>(document);
if ( json["td"].toInt() )
layer_indices[index] = layer.get();
deferred.emplace_back(layer.get(), json);
inner_shape = std::move(layer);
}

if ( start_mask )
{
auto layer = std::make_unique<model::Layer>(document);
mask = layer.get();
layer->name.set(json["nm"].toString());
layer->shapes.insert(std::move(inner_shape), 0);
composition->shapes.insert(std::move(layer), 0);
}
else
{
auto tt = json["tt"].toInt();

if ( mask && tt )
{
mask = layer.get();
layer->mask->mask.set(true);
layer->name.set(json["nm"].toString());
auto child = std::make_unique<model::Layer>(document);
layer_indices[index] = child.get();
deferred.emplace_back(child.get(), json);
layer->shapes.insert(std::move(child), 0);
composition->shapes.insert(std::move(layer), 0);
mask->shapes.insert(std::move(inner_shape), 1);
auto mode = model::MaskSettings::MaskMode((tt + 1) / 2);
mask->mask->mask.set(mode);
mask->mask->inverted.set(tt > 0 && tt % 2 == 0);
}
else
{
layer_indices[index] = layer.get();
deferred.emplace_back(layer.get(), json);
if ( has_mask(json) )
mask->shapes.insert(std::move(layer), 1);
else
composition->shapes.insert(std::move(layer), 0);
mask = nullptr;
composition->shapes.insert(std::move(inner_shape), 0);
}
mask = nullptr;
}
}

void load_precomp_layer(const QJsonObject& json, std::set<int>& referenced)
std::unique_ptr<model::PreCompLayer> load_precomp_layer(const QJsonObject& json)
{
auto props = load_basic_setup(json);

Expand Down Expand Up @@ -211,27 +232,9 @@ class LottieImporterState
json["h"].toInt()
));

int index = json["ind"].toInt();
auto op = document->main()->animation->last_frame.get();
if ( json.contains("parent") || referenced.count(index) || json["ip"].toDouble() != 0 ||
json["op"].toDouble(op) != op || has_mask(json)
)
{
auto layer = make_node<model::Layer>(document);
layer->name.set(precomp->name.get());
layer->shapes.insert(std::move(precomp), 0);
if ( has_mask(json) )
layer->mask->mask.set(mask);
layer_indices[index] = layer.get();
deferred.emplace_back(layer.get(), json);
load_transform(json["ks"].toObject(), layer->transform.get(), &layer->opacity);
composition->shapes.insert(std::move(layer), 0);
}
else
{
load_transform(json["ks"].toObject(), precomp->transform.get(), &precomp->opacity);
composition->shapes.insert(std::move(precomp), 0);
}
load_transform(json["ks"].toObject(), precomp->transform.get(), &precomp->opacity);

return precomp;
}

void load_mask(const QJsonObject& json, model::Group* group)
Expand Down Expand Up @@ -318,7 +321,7 @@ class LottieImporterState
auto masks = json["masksProperties"].toArray();
if ( !masks.empty() )
{
layer->mask->mask.set(true);
layer->mask->mask.set(model::MaskSettings::Alpha);

auto clip_p = make_node<model::Group>(document);
auto clip = clip_p.get();
Expand Down
2 changes: 1 addition & 1 deletion src/core/io/svg/svg_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ class glaxnimate::io::svg::SvgParser::Private
auto layer = std::make_unique<model::Layer>(document);
apply_common_style(layer.get(), args.element, style);
set_name(layer.get(), args.element);
layer->mask->mask.set(true);
layer->mask->mask.set(model::MaskSettings::Alpha);

QDomElement element = args.element;

Expand Down
12 changes: 10 additions & 2 deletions src/core/model/mask_settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,16 @@ class MaskSettings : public Object
{
GLAXNIMATE_OBJECT(MaskSettings)

GLAXNIMATE_PROPERTY(bool, mask, false, {}, {}, PropertyTraits::Visual)
// GLAXNIMATE_PROPERTY(bool, invert, false, {}, {}, PropertyTraits::Visual)
public:
enum MaskMode
{
NoMask = 0,
Alpha = 1,
};
Q_ENUM(MaskMode)

GLAXNIMATE_PROPERTY(MaskMode, mask, NoMask, {}, {}, PropertyTraits::Visual)
GLAXNIMATE_PROPERTY(bool, inverted, false, {}, {}, PropertyTraits::Visual)

public:
using Object::Object;
Expand Down
9 changes: 9 additions & 0 deletions src/core/model/shapes/layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <QPainter>

#include "model/composition.hpp"
#include "model/document.hpp"

GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::Layer)

Expand Down Expand Up @@ -124,6 +125,14 @@ void glaxnimate::model::Layer::paint(QPainter* painter, FrameTime time, PaintMod
{
QPainterPath clip = shapes[0]->to_clip(time);
clip.setFillRule(Qt::WindingFill);
if ( mask->inverted.get() )
{
QPainterPath outer_clip;
outer_clip.addPolygon(
transform.inverted().map(QRectF(QPointF(0, 0), document()->size()))
);
clip = outer_clip.subtracted(clip);
}
painter->setClipPath(clip, Qt::IntersectClip);
}

Expand Down
2 changes: 1 addition & 1 deletion src/python/miscdefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ static void define_trace(py::module& m)
py::class_<TraceOptions>(trace, "TraceOptions")
.def(py::init<>())
.def_property("smoothness", &TraceOptions::smoothness, &TraceOptions::set_smoothness)
.def_property("min_area", &TraceOptions::min_area, &TraceOptions::min_area)
.def_property("min_area", &TraceOptions::min_area, &TraceOptions::set_min_area)
;
py::class_<Tracer>(trace, "Tracer")
.def(py::init<const QImage&, const TraceOptions&>())
Expand Down

0 comments on commit 009a92f

Please sign in to comment.