File indexing completed on 2025-01-05 04:01:18

0001 /*
0002  * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best>
0003  *
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  */
0006 
0007 #include "rive_loader.hpp"
0008 
0009 #include "io/animated_properties.hpp"
0010 #include "model/assets/assets.hpp"
0011 #include "model/shapes/precomp_layer.hpp"
0012 #include "model/shapes/rect.hpp"
0013 #include "model/shapes/ellipse.hpp"
0014 #include "model/shapes/polystar.hpp"
0015 #include "model/shapes/path.hpp"
0016 #include "model/shapes/fill.hpp"
0017 #include "model/shapes/stroke.hpp"
0018 #include "model/shapes/image.hpp"
0019 
0020 using namespace glaxnimate;
0021 using namespace glaxnimate::io;
0022 using namespace glaxnimate::io::rive;
0023 
0024 namespace {
0025 
0026 struct Artboard
0027 {
0028     Artboard() = default;
0029 
0030     Artboard(Object* first, Object* last)
0031         : object(first),
0032             children(object),
0033             child_count(last - first + 1)
0034     {}
0035 
0036     Object* operator->() const { return object; }
0037 
0038     Object* object = nullptr;
0039     Object* children = nullptr;
0040     Identifier child_count = 0;
0041     VarUint timeline_duration = 0;
0042     VarUint keyframe_timeline_duration = 0;
0043     model::Composition* comp = nullptr;
0044     QSizeF size;
0045 };
0046 
0047 template<class T> T load_property_get_keyframe(const detail::JoinedPropertyKeyframe& kf, std::size_t index);
0048 template<> Float32 load_property_get_keyframe<Float32>(const detail::JoinedPropertyKeyframe& kf, std::size_t index)
0049 {
0050     return kf.values[index].vector()[0];
0051 }
0052 template<> VarUint load_property_get_keyframe<VarUint>(const detail::JoinedPropertyKeyframe& kf, std::size_t index)
0053 {
0054     return kf.values[index].vector()[0];
0055 }
0056 template<> QColor load_property_get_keyframe<QColor>(const detail::JoinedPropertyKeyframe& kf, std::size_t index)
0057 {
0058     return kf.values[index].color();
0059 }
0060 
0061 template<class... T, class PropT, class Func, std::size_t... Ind, std::size_t N>
0062 void load_property_impl(Object* rive, PropT& property, const detail::AnimatedProperties& animations,
0063                     const std::array<const char*, N>& names, T... defvals, const Func& value_func, std::index_sequence<Ind...>)
0064 {
0065     property.set(value_func(rive->get<T>(names[Ind], defvals)...));
0066 
0067     for ( const auto& kf : animations.joined(std::vector<QString>(names.begin(), names.end())) )
0068         property.set_keyframe(kf.time, value_func(load_property_get_keyframe<T>(kf, Ind)...))->set_transition(kf.transition);
0069 
0070 }
0071 
0072 template<class... T, class PropT, class Func>
0073 void load_property(Object* rive, PropT& property, const detail::AnimatedProperties& animations,
0074                     const std::array<const char*, sizeof...(T)>& names, T... defvals, const Func& value_func)
0075 {
0076     load_property_impl<T...>(rive, property, animations, names, defvals..., value_func, std::index_sequence_for<T...>{});
0077 }
0078 
0079 template<std::size_t RetInd, class... T, class PropT, class Func, std::size_t... Ind, std::size_t N>
0080 void load_property_vector_impl(Object* rive, PropT& property, const detail::AnimatedProperties& animations,
0081                     const std::array<const char*, N>& names, T... defvals, const Func& value_func, std::index_sequence<Ind...>)
0082 {
0083     property.set(
0084         std::get<RetInd>(
0085             value_func(rive->get<T>(names[Ind], defvals)...)
0086         )
0087     );
0088 
0089     for ( const auto& kf : animations.joined(std::vector<QString>(names.begin(), names.end())) )
0090         property.set_keyframe(kf.time,
0091             std::get<RetInd>(value_func(load_property_get_keyframe<T>(kf, Ind)...))
0092         )->set_transition(kf.transition);
0093 
0094 }
0095 
0096 
0097 template<class T>
0098 void expand(T...) {}
0099 
0100 template<class... T, class... PropT, class Func, std::size_t... Ind, std::size_t... PropInd, std::size_t N>
0101 void load_properties_impl(Object* rive, const std::tuple<PropT...>& properties, const detail::AnimatedProperties& animations,
0102                     const std::array<const char*, N>& names, T... defvals, const Func& value_func,
0103                     std::index_sequence<Ind...> ind, std::index_sequence<PropInd...>)
0104 {
0105     expand(
0106         (load_property_vector_impl<PropInd, T...>(
0107             rive, *std::get<PropInd>(properties), animations, names, defvals..., value_func, ind
0108         ), 0) ...
0109     );
0110 }
0111 
0112 template<class... T, class... PropT, class Func>
0113 void load_properties(
0114     Object* rive, std::tuple<PropT...> properties, const detail::AnimatedProperties& animations,
0115     const std::array<const char*, sizeof...(T)>& names, T... defvals, const Func& value_func)
0116 {
0117     load_properties_impl<T...>(
0118         rive, properties, animations, names, defvals..., value_func,
0119         std::index_sequence_for<T...>{}, std::index_sequence_for<PropT...>{});
0120 }
0121 
0122 template<class T, class PropT>
0123 void load_property(Object* rive, PropT& property, const detail::AnimatedProperties& animations, const char* name, T defval = {})
0124 {
0125     property.set(rive->get<T>(name, defval));
0126 
0127     for ( const auto& kf : animations.joined({name}) )
0128         property.set_keyframe(kf.time, load_property_get_keyframe<T>(kf, 0))->set_transition(kf.transition);
0129 }
0130 
0131 QPointF make_point(Float32 x, Float32 y)
0132 {
0133     return QPointF(x, y);
0134 }
0135 
0136 struct Asset
0137 {
0138     Object* object = nullptr;
0139     model::Asset* asset = nullptr;
0140 };
0141 
0142 struct LoadCotext
0143 {
0144     void new_artboard(Object* object)
0145     {
0146         artboards[object] = Artboard(object, &objects.back());
0147         artboard = &artboards[object];
0148         artboards_id.push_back(artboard);
0149         artboard->comp = document->assets()->compositions->values.insert(std::make_unique<model::Composition>(document));
0150         artboard->size = QSizeF(
0151             object->get<Float32>("width"),
0152             object->get<Float32>("height")
0153         );
0154     }
0155 
0156     Object* artboard_child(Identifier id) const
0157     {
0158         if ( artboard && id < artboard->child_count )
0159             return artboard->children + id;
0160         return nullptr;
0161     }
0162 
0163     LoadCotext(RiveFormat* format, model::Document* document)
0164         : document(document), format(format)
0165     {
0166         main = document->assets()->compositions->values.insert(std::make_unique<model::Composition>(document));
0167     }
0168 
0169     void preprocess_object(Object* object)
0170     {
0171         if ( object->type().id == TypeId::Artboard )
0172         {
0173             new_artboard(object);
0174         }
0175         else if ( object->type().id == TypeId::KeyedObject )
0176         {
0177             if ( !artboard )
0178             {
0179                 format->warning(i18n("Unexpected Keyed Object"));
0180                 return;
0181             }
0182             auto id = object->get<Identifier>("objectId", artboard->child_count);
0183             keyed_object = artboard_child(id);
0184             keyed_property = nullptr;
0185             if ( !keyed_object )
0186             {
0187                 format->warning(i18n("Invalid Keyed Object id %1", id));
0188                 return;
0189             }
0190         }
0191         else if ( object->type().id == TypeId::KeyedProperty )
0192         {
0193             if ( !keyed_object )
0194             {
0195                 format->warning(i18n("Unexpected Keyed Property"));
0196                 return;
0197             }
0198 
0199             auto id = object->get<Identifier>("propertyKey");
0200             auto prop = keyed_object->type().property(id);
0201 
0202             if ( !prop )
0203             {
0204                 format->warning(i18n("Unknown Keyed Property id %1", id));
0205                 return;
0206             }
0207 
0208             keyed_object->animations().push_back({prop, {}});
0209             keyed_property = &keyed_object->animations().back();
0210         }
0211         else if ( object->type().id == TypeId::LinearAnimation )
0212         {
0213             if ( !artboard )
0214             {
0215                 format->warning(i18n("Unexpected Animation"));
0216                 return;
0217             }
0218 
0219             auto duration = object->get<VarUint>("duration");
0220             if ( duration > artboard->timeline_duration )
0221                 artboard->timeline_duration = duration;
0222         }
0223         else if ( object->type().id == TypeId::ImageAsset )
0224         {
0225             assets.push_back({object, load_image_asset(object)});
0226         }
0227         else if ( object->type().id == TypeId::FileAssetContents )
0228         {
0229             if ( assets.empty() )
0230             {
0231                 format->warning(i18n("Unexpected Asset Contents"));
0232                 return;
0233             }
0234 
0235             auto data = object->get<QByteArray>("bytes");
0236             if ( data.isEmpty() )
0237                 return;
0238 
0239             if ( auto img = qobject_cast<model::Bitmap*>(assets.back().asset) )
0240             {
0241                 if ( !img->from_raw_data(data) )
0242                     format->warning(i18n("Invalid Image Data"));
0243             }
0244         }
0245         else if ( object->has_type(TypeId::Asset) )
0246         {
0247             assets.push_back({object, nullptr});
0248         }
0249         else if ( object->has_type(TypeId::KeyFrame) )
0250         {
0251             if ( !keyed_property )
0252             {
0253                 format->warning(i18n("Unexpected Keyframe"));
0254                 return;
0255             }
0256 
0257             auto frame = object->get<VarUint>("duration");
0258             if ( frame > artboard->keyframe_timeline_duration )
0259                 artboard->keyframe_timeline_duration = frame;
0260 
0261             keyed_property->keyframes.push_back(object);
0262         }
0263         else if ( object->has("parentId") )
0264         {
0265             auto parent_id = object->get<Identifier>("parentId");
0266             auto parent = artboard_child(parent_id);
0267             if ( !parent )
0268                 format->warning(i18n("Could not find parent with id %1", parent_id));
0269             else
0270                 parent->children().push_back(object);
0271         }
0272     }
0273 
0274     void process_object(Object* object)
0275     {
0276         if ( object->type().id == TypeId::Artboard )
0277         {
0278             process_artboard(object);
0279         }
0280     }
0281 
0282     void process_artboard(Object* object)
0283     {
0284         const auto& artboard = artboards.at(object);
0285 
0286         artboard.comp->name.set(object->get<QString>("name"));
0287         add_shapes(object, artboard.comp->shapes);
0288 
0289         auto precomp_layer = std::make_unique<model::PreCompLayer>(document);
0290         precomp_layer->name.set(artboard.comp->name.get());
0291         precomp_layer->size.set(artboard.size.toSize());
0292         detail::AnimatedProperties animations = load_animations(object);
0293         load_transform(object, precomp_layer->transform.get(), animations, QRectF(QPointF(0, 0), artboard.size));
0294         precomp_layer->opacity.set(object->get<Float32>("opacity", 1));
0295         precomp_layer->composition.set(artboard.comp);
0296 
0297         float last_frame = artboard.timeline_duration == 0 ? artboard.keyframe_timeline_duration : artboard.timeline_duration;
0298         main->animation->last_frame.set(qMax(main->animation->last_frame.get(), last_frame));
0299 
0300         if ( document->assets()->compositions->values.size() == 1 )
0301         {
0302             main->width.set(precomp_layer->size.get().width());
0303             main->height.set(precomp_layer->size.get().height());
0304         }
0305 
0306         main->shapes.insert(std::move(precomp_layer));
0307     }
0308 
0309     void add_shapes(Object* parent, model::ObjectListProperty<model::ShapeElement>& prop)
0310     {
0311 
0312         std::vector<std::unique_ptr<model::ShapeElement>> shapes;
0313 
0314         for ( Object* child : parent->children() )
0315         {
0316             if ( child == parent )
0317             {
0318                 format->error(i18n("Parent circular reference detected"));
0319                 continue;
0320             }
0321 
0322             auto shape = load_shape(child);
0323             if ( shape )
0324             {
0325                 if ( child->has_type(TypeId::Node) )
0326                     shapes.emplace_back(std::move(shape));
0327                 else
0328                     prop.insert(std::move(shape));
0329             }
0330         }
0331 
0332         for ( auto it = shapes.rbegin(); it != shapes.rend(); ++it )
0333             prop.insert(std::move(*it));
0334     }
0335 
0336     std::unique_ptr<model::Layer> load_shape_layer(Object* shape, const detail::AnimatedProperties& animations)
0337     {
0338         auto layer = std::make_unique<model::Layer>(document);
0339         load_shape_group(shape, layer.get(), animations);
0340         return layer;
0341     }
0342 
0343     void load_transform(Object* rive, model::Transform* transform, const detail::AnimatedProperties& animations, const QRectF& bbox)
0344     {
0345         load_property<Float32, Float32>(rive, transform->position, animations, {"x", "y"}, 0, 0, &make_point);
0346 
0347         if ( rive->type().property("originX") )
0348         {
0349             load_property<Float32, Float32>(rive, transform->anchor_point, animations, {"originX", "originY"}, 0.5, 0.5,
0350                 [&bbox](Float32 ox, Float32 oy){
0351                     return QPointF(
0352                         math::lerp(bbox.left(), bbox.right(), ox),
0353                         math::lerp(bbox.top(), bbox.bottom(), oy)
0354                     );
0355                 }
0356             );
0357         }
0358 
0359         /*load_properties<Float32, Float32, Float32, Float32>(
0360             rive,
0361             std::make_tuple(&transform->position, &transform->anchor_point),
0362             animations,
0363             {"x", "y", "originX", "originY"},
0364             0, 0, 0.5, 0.5,
0365             [&bbox] ( Float32 x, Float32 y, Float32 ox, Float32 oy ) {
0366                 QPointF anchor(
0367                     math::lerp(bbox.left(), bbox.right(), ox),
0368                     math::lerp(bbox.top(), bbox.bottom(), oy)
0369                 );
0370                 return std::make_tuple(QPointF(x, y) - anchor, anchor);
0371             }
0372         );*/
0373         load_property<Float32>(rive, transform->rotation, animations, "rotation");
0374         load_property<Float32, Float32>(rive, transform->scale, animations, {"scaleX", "scaleX"}, 1, 1, [](Float32 x, Float32 y){
0375             return QVector2D(x, y);
0376         });
0377     }
0378 
0379     void load_shape_group(Object* shape, model::Group* group, const detail::AnimatedProperties& animations)
0380     {
0381         load_property<Float32>(shape, group->opacity, animations, "opacity", 1);
0382         group->name.set(shape->get<QString>("name"));
0383         add_shapes(shape, group->shapes);
0384         auto box = group->local_bounding_rect(0);
0385         load_transform(shape, group->transform.get(), animations, box);
0386     }
0387 
0388     std::unique_ptr<model::ShapeElement> load_shape(Object* object)
0389     {
0390         detail::AnimatedProperties animations = load_animations(object);
0391 
0392         switch ( object->type().id )
0393         {
0394             case TypeId::Shape:
0395             case TypeId::Node:
0396                 return load_shape_layer(object, animations);
0397             case TypeId::Rectangle:
0398                 return load_rectangle(object, animations);
0399             case TypeId::Ellipse:
0400                 return load_ellipse(object, animations);
0401             case TypeId::Fill:
0402                 return load_fill(object, animations);
0403             case TypeId::Stroke:
0404                 return load_stroke(object, animations);
0405             case TypeId::Polygon:
0406                 return load_polygon(object, animations, model::PolyStar::Polygon);
0407             case TypeId::Star:
0408                 return load_polygon(object, animations, model::PolyStar::Star);
0409             case TypeId::Triangle:
0410                 return load_triangle(object, animations);
0411             case TypeId::PointsPath:
0412                 return load_path(object, animations);
0413             case TypeId::NestedArtboard:
0414                 return load_precomp(object, animations);
0415             case TypeId::Image:
0416                 return load_image(object, animations);
0417             case TypeId::TrimPath:
0418             case TypeId::Bone:
0419             case TypeId::RootBone:
0420             case TypeId::ClippingShape:
0421             case TypeId::Text:
0422                 /// \todo
0423             default:
0424                 return {};
0425         }
0426     }
0427 
0428     std::unique_ptr<model::Group> load_rectangle(Object* object, const detail::AnimatedProperties& animations)
0429     {
0430         auto group = std::make_unique<model::Group>(document);
0431         auto shape = std::make_unique<model::Rect>(document);
0432         shape->name.set(object->get<QString>("name"));
0433 
0434         load_property<Float32, Float32, Float32, Float32>(object, shape->rounded, animations,
0435             {"cornerRadiusTL", "cornerRadiusBL", "cornerRadiusBR", "cornerRadiusTR"},
0436             0, 0, 0, 0,
0437             [](Float32 tl, Float32 bl, Float32 br, Float32 tr){
0438                 return (tl + bl + br + tr) / 4;
0439             }
0440         );
0441 
0442         load_property<Float32, Float32>(object, shape->size, animations, {"width", "height"}, 0, 0, [](Float32 x, Float32 y){
0443             return QSizeF(x, y);
0444         });
0445 
0446         group->shapes.insert(std::move(shape));
0447         load_shape_group(object, group.get(), animations);
0448         return group;
0449     }
0450 
0451     std::unique_ptr<model::Group> load_ellipse(Object* object, const detail::AnimatedProperties& animations)
0452     {
0453         auto group = std::make_unique<model::Group>(document);
0454         auto shape = std::make_unique<model::Ellipse>(document);
0455         shape->name.set(object->get<QString>("name"));
0456 
0457 
0458         load_property<Float32, Float32>(object, shape->size, animations, {"width", "height"}, 0, 0, [](Float32 x, Float32 y){
0459             return QSizeF(x, y);
0460         });
0461 
0462         group->shapes.insert(std::move(shape));
0463         load_shape_group(object, group.get(), animations);
0464         return group;
0465     }
0466 
0467     std::unique_ptr<model::Fill> load_fill(Object* object, const detail::AnimatedProperties& animations)
0468     {
0469         auto shape = std::make_unique<model::Fill>(document);
0470         load_styler(object, shape.get(), animations);
0471         /// \todo fillRule
0472         return shape;
0473     }
0474 
0475     std::unique_ptr<model::Stroke> load_stroke(Object* object, const detail::AnimatedProperties& animations)
0476     {
0477         auto shape = std::make_unique<model::Stroke>(document);
0478         load_styler(object, shape.get(), animations);
0479         load_property<Float32>(object, shape->width, animations, "thickness");
0480         /// \todo cap + join
0481         return shape;
0482     }
0483 
0484     void load_styler(Object* object, model::Styler* shape, const detail::AnimatedProperties& animations)
0485     {
0486         shape->name.set(object->get<QString>("name"));
0487         shape->visible.set(object->get<bool>("isVisible", true));
0488         load_property<Float32>(object, shape->opacity, animations, "opacity", 1);
0489 
0490         for ( const auto& child : object->children() )
0491         {
0492             if ( child->type().id == TypeId::SolidColor )
0493                 load_property<QColor>(child, shape->color, load_animations(child), "colorValue", QColor("#747474"));
0494             else if ( child->type().id == TypeId::LinearGradient )
0495                 shape->use.set(load_gradient(child, model::Gradient::Linear));
0496             else if ( child->type().id == TypeId::RadialGradient )
0497                 shape->use.set(load_gradient(child, model::Gradient::Radial));
0498         }
0499     }
0500 
0501     model::Gradient* load_gradient(Object* object, model::Gradient::GradientType type)
0502     {
0503         auto colors = std::make_unique<glaxnimate::model::GradientColors>(document);
0504         colors->name.set(object->get<QString>("name"));
0505         auto colors_ptr = colors.get();
0506         document->assets()->gradient_colors->values.insert(std::move(colors));
0507 
0508         auto gradient = std::make_unique<glaxnimate::model::Gradient>(document);
0509         gradient->name.set(object->get<QString>("name"));
0510         gradient->colors.set(colors_ptr);
0511         gradient->type.set(type);
0512 
0513         auto animations = load_animations(object);
0514         load_property<Float32, Float32>(object, gradient->start_point, animations, {"startX", "startY"}, 0, 0, &make_point);
0515         load_property<Float32, Float32>(object, gradient->end_point, animations, {"endX", "endY"}, 0, 0, &make_point);
0516 
0517         /// \todo color animations
0518         QGradientStops stops;
0519         for ( const auto& child : object->children() )
0520         {
0521             if ( child->type().id == TypeId::GradientStop )
0522             {
0523                 stops.push_back({
0524                     child->get<Float32>("position"),
0525                     child->get<QColor>("colorValue"),
0526                 });
0527             }
0528         }
0529         colors_ptr->colors.set(stops);
0530 
0531         auto ptr = gradient.get();
0532         document->assets()->gradients->values.insert(std::move(gradient));
0533         return ptr;
0534     }
0535 
0536     detail::AnimatedProperties load_animations(Object* object)
0537     {
0538         using namespace glaxnimate::io::detail;
0539 
0540         AnimatedProperties props;
0541         for ( const auto& anim : object->animations() )
0542         {
0543             AnimatedProperty& prop = props.properties[anim.property->name];
0544             for ( auto kf : anim.keyframes )
0545             {
0546                 model::KeyframeTransition transition; /// \todo
0547                 prop.keyframes.push_back({
0548                     kf->get<Float32>("frame", 0),
0549                     ValueVariant(kf->get_variant("value")),
0550                     transition
0551                 });
0552             }
0553         }
0554         return props;
0555     }
0556 
0557     std::unique_ptr<model::Group> load_polygon(Object* object, const detail::AnimatedProperties& animations, model::PolyStar::StarType type)
0558     {
0559         auto group = std::make_unique<model::Group>(document);
0560         load_shape_group(object, group.get(), animations);
0561 
0562         auto shape = std::make_unique<model::PolyStar>(document);
0563         shape->name.set(object->get<QString>("name"));
0564         shape->type.set(type);
0565         /// \todo cornerRadius
0566         load_property<VarUint>(object, shape->points, animations, "points", 5);
0567         shape->outer_radius.set(100);
0568 
0569         load_property<Float32>(object, shape->inner_radius, animations, {"innerRadius"}, 0.5, [](Float32 pc){
0570             return pc * 100;
0571         });
0572 
0573         load_property<Float32>(object, shape->points, animations, "points", 5);
0574 
0575 
0576         load_property<Float32, Float32, Float32, Float32>(object, group->transform->scale, animations,
0577             {"scaleX", "scaleY", "width", "height"},
0578             1, 1, 0, 0, [](Float32 sx, Float32 sy, Float32 w, Float32 h){
0579             return QVector2D(w / 200 * sx, h / 200 * sy);
0580         });
0581 
0582         group->shapes.insert(std::move(shape));
0583         return group;
0584     }
0585 
0586     std::unique_ptr<model::Group> load_triangle(Object* object, const detail::AnimatedProperties& animations)
0587     {
0588         auto group = std::make_unique<model::Group>(document);
0589         auto shape = std::make_unique<model::Path>(document);
0590         shape->name.set(object->get<QString>("name"));
0591 
0592 
0593         load_property<Float32, Float32>(object, shape->shape, animations, {"width", "height"}, 0, 0, [](Float32 w, Float32 h){
0594             math::bezier::Bezier path;
0595             path.add_point({-w/2, h/2});
0596             path.add_point({0, -h/2});
0597             path.add_point({w/2, h/2});
0598             path.close();
0599             return path;
0600         });
0601 
0602         group->shapes.insert(std::move(shape));
0603         load_shape_group(object, group.get(), animations);
0604         return group;
0605     }
0606 
0607     std::unique_ptr<model::Path> load_path(Object* object, const detail::AnimatedProperties& animations)
0608     {
0609         auto shape = std::make_unique<model::Path>(document);
0610         shape->name.set(object->get<QString>("name"));
0611         bool closed = object->get<bool>("isClosed");
0612         shape->closed.set(closed);
0613 
0614         math::bezier::Bezier bez;
0615         for ( const auto& child : object->children() )
0616         {
0617             math::bezier::Point p;
0618             p.pos = QPointF(child->get<Float32>("x", 0), child->get<Float32>("y", 0));
0619             if ( child->type().id == TypeId::CubicMirroredVertex )
0620             {
0621                 p.type = math::bezier::Symmetrical;
0622                 auto tangent = math::from_polar<QPointF>(
0623                     child->get<Float32>("distance"),
0624                     child->get<Float32>("rotation")
0625                 );
0626                 p.tan_in = p.pos - tangent;
0627                 p.tan_out = p.pos + tangent;
0628             }
0629             else if ( child->type().id == TypeId::CubicAsymmetricVertex )
0630             {
0631                 p.type = math::bezier::Smooth;
0632                 p.tan_in = p.pos - math::from_polar<QPointF>(
0633                     child->get<Float32>("inDistance"),
0634                     child->get<Float32>("rotation")
0635                 );
0636                 p.tan_out = p.pos + math::from_polar<QPointF>(
0637                     child->get<Float32>("outDistance"),
0638                     child->get<Float32>("rotation")
0639                 );
0640             }
0641             else if ( child->type().id == TypeId::CubicDetachedVertex )
0642             {
0643                 p.type = math::bezier::Corner;
0644                 p.tan_in = p.pos + math::from_polar<QPointF>(
0645                     child->get<Float32>("inDistance"),
0646                     child->get<Float32>("inRotation")
0647                 );
0648                 p.tan_out = p.pos + math::from_polar<QPointF>(
0649                     child->get<Float32>("outDistance"),
0650                     child->get<Float32>("outRotation")
0651                 );
0652             }
0653             else if ( child->type().id == TypeId::StraightVertex )
0654             {
0655                 p.type = math::bezier::Corner;
0656                 p.tan_in = p.tan_out = p.pos;
0657             }
0658             else
0659             {
0660                 continue;
0661             }
0662 
0663             bez.push_back(p);
0664         }
0665 
0666         bez.set_closed(closed);
0667 
0668         /// \todo animation
0669         Q_UNUSED(animations);
0670         shape->shape.set(bez);
0671 
0672         return shape;
0673     }
0674 
0675     std::unique_ptr<model::PreCompLayer> load_precomp(Object* object, const detail::AnimatedProperties& animations)
0676     {
0677         auto shape = std::make_unique<model::PreCompLayer>(document);
0678         shape->name.set(object->get<QString>("name"));
0679         load_property<Float32>(object, shape->opacity, animations, "opacity", 1);
0680 
0681         QRectF box;
0682 
0683         if ( object->has("artboardId") )
0684         {
0685             auto id = object->get<VarUint>("artboardId");
0686             shape->size.set(artboards_id[id]->size);
0687             shape->composition.set(artboards_id[id]->comp);
0688             box.setSize(artboards_id[id]->size);
0689         }
0690 
0691         load_transform(object, shape->transform.get(), animations, box);
0692         return shape;
0693     }
0694 
0695     model::Bitmap* load_image_asset(Object* object)
0696     {
0697         auto image = std::make_unique<glaxnimate::model::Bitmap>(document);
0698         image->filename.set(object->get<QString>("name"));
0699         image->width.set(object->get<Float32>("width"));
0700         image->height.set(object->get<Float32>("height"));
0701         auto ptr = image.get();
0702         document->assets()->images->values.insert(std::move(image));
0703         return ptr;
0704     }
0705 
0706     std::unique_ptr<model::Image> load_image(Object* object, const detail::AnimatedProperties& animations)
0707     {
0708         auto shape = std::make_unique<model::Image>(document);
0709         shape->name.set(object->get<QString>("name"));
0710         auto id = object->get<VarUint>("assetId");
0711         QSizeF size;
0712         if ( auto bmp = qobject_cast<model::Bitmap*>(assets[id].asset) )
0713         {
0714             size = bmp->size();
0715             shape->transform->anchor_point.set(QPointF(
0716                 bmp->width.get() / 2.,
0717                 bmp->height.get() / 2.
0718             ));
0719             shape->image.set(bmp);
0720         }
0721         load_transform(object, shape->transform.get(), animations, {QPointF(0, 0), size});
0722 
0723         return shape;
0724     }
0725 
0726     model::Document* document = nullptr;
0727     std::map<Object*, Artboard> artboards;
0728     std::vector<Object> objects;
0729     Artboard* artboard = nullptr;
0730     Object* keyed_object = nullptr;
0731     PropertyAnimation* keyed_property = nullptr;
0732     RiveFormat* format = nullptr;
0733     std::vector<Artboard*> artboards_id;
0734     std::vector<Asset> assets;
0735     model::Composition* main = nullptr;
0736 };
0737 
0738 } // namespace
0739 
0740 RiveLoader::RiveLoader(BinaryInputStream& stream, RiveFormat* format)
0741     : stream(stream),
0742     format(format)
0743 {
0744     extra_props = read_property_table();
0745     QObject::connect(&types, &TypeSystem::type_not_found, [format](int type){
0746         format->error(i18n("Unknown object of type %1", int(type)));
0747     });
0748 
0749     if ( stream.has_error() )
0750         format->error(i18n("Could not read property table"));
0751 
0752 }
0753 
0754 std::vector<Object> RiveLoader::load_object_list()
0755 {
0756     if ( stream.has_error() )
0757         return {};
0758 
0759     std::vector<Object> objects;
0760     while ( !stream.has_error() && !stream.eof() )
0761         objects.emplace_back(read_object());
0762     return objects;
0763 }
0764 
0765 bool RiveLoader::load_document(model::Document* document)
0766 {
0767     if ( stream.has_error() )
0768         return false;
0769 
0770     LoadCotext context(format, document);
0771 
0772     while ( !stream.has_error() && !stream.eof() )
0773         if ( auto obj = read_object() )
0774             context.objects.emplace_back(std::move(obj));
0775 
0776     for ( auto& object : context.objects )
0777         context.preprocess_object(&object);
0778 
0779 
0780     for ( auto& object : context.objects )
0781         context.process_object(&object);
0782 
0783     return true;
0784 }
0785 
0786 Object RiveLoader::read_object()
0787 {
0788     auto type_id = TypeId(stream.read_uint_leb128());
0789     if ( stream.has_error() )
0790     {
0791         format->error(i18n("Could not load object type ID"));
0792         return {};
0793     }
0794 
0795     Object obj = types.object(type_id);
0796 
0797     if ( !obj )
0798         return {};
0799 
0800     while ( true )
0801     {
0802         Identifier prop_id = stream.read_uint_leb128();
0803         if ( stream.has_error() )
0804         {
0805             format->error(i18n("Could not load property ID in %1 (%2)")
0806                 .arg(int(type_id)).arg(obj.definition()->name));
0807             return {};
0808         }
0809 
0810         if ( prop_id == 0 )
0811             break;
0812 
0813         auto prop_def = obj.type().property(prop_id);
0814         if ( !prop_def )
0815         {
0816             auto unknown_it = extra_props.find(prop_id);
0817             if ( unknown_it == extra_props.end() )
0818             {
0819                 format->error(i18n("Unknown property %1 of %2 (%3)")
0820                     .arg(prop_id).arg(int(type_id)).arg(obj.definition()->name));
0821                 return {};
0822             }
0823             else
0824             {
0825                 format->warning(i18n("Skipping unknown property %1 of %2 (%3)")
0826                         .arg(prop_id).arg(int(type_id)).arg(obj.definition()->name));
0827             }
0828         }
0829         else
0830         {
0831             obj.set(prop_def, read_property_value(prop_def->type));
0832             if ( stream.has_error() )
0833             {
0834                 format->error(i18n("Error loading property %1 (%2) of %3 (%4)")
0835                     .arg(prop_id).arg(prop_def->name).arg(int(type_id)).arg(obj.definition()->name));
0836                 return {};
0837             }
0838         }
0839     }
0840 
0841     return obj;
0842 }
0843 
0844 
0845 QVariant RiveLoader::read_property_value(PropertyType type)
0846 {
0847     switch ( type )
0848     {
0849         case PropertyType::Bool:
0850             return bool(stream.next());
0851         case PropertyType::Bytes:
0852             return read_raw_string();
0853         case PropertyType::String:
0854             return read_string_utf8();
0855         case PropertyType::VarUint:
0856             return QVariant::fromValue(stream.read_uint_leb128());
0857         case PropertyType::Float:
0858             return stream.read_float32_le();
0859         case PropertyType::Color:
0860             return QColor::fromRgba(stream.read_uint32_le());
0861     }
0862 
0863     return {};
0864 }
0865 
0866 
0867 PropertyTable RiveLoader::read_property_table()
0868 {
0869     std::vector<VarUint> props;
0870     while ( true )
0871     {
0872         VarUint id = stream.read_uint_leb128();
0873         if ( stream.has_error() )
0874             return {};
0875 
0876         if ( id == 0 )
0877             break;
0878 
0879         props.push_back(id);
0880     }
0881 
0882     quint32 current_int = 0;
0883     quint32 bit = 8;
0884 
0885     PropertyTable table;
0886 
0887     for ( auto id : props )
0888     {
0889         if ( bit == 8 )
0890         {
0891             current_int = stream.read_uint32_le();
0892             if ( stream.has_error() )
0893                 return {};
0894             bit = 0;
0895         }
0896 
0897         int type = (current_int >> bit) & 3;
0898         if ( type == 0 )
0899             table[id] = PropertyType::VarUint;
0900         else if ( type == 1 )
0901             table[id] = PropertyType::String;
0902         else if ( type == 2 )
0903             table[id] = PropertyType::Float;
0904         else if ( type == 3 )
0905             table[id] = PropertyType::Color;
0906 
0907         bit += 2;
0908     }
0909 
0910     return table;
0911 }
0912 
0913 void RiveLoader::skip_value(glaxnimate::io::rive::PropertyType type)
0914 {
0915     switch ( type )
0916     {
0917         case PropertyType::Bool:
0918         case PropertyType::VarUint:
0919             stream.read_uint_leb128();
0920             break;
0921         case PropertyType::Bytes:
0922         case PropertyType::String:
0923             read_raw_string();
0924             break;
0925         case PropertyType::Float:
0926             stream.read_float32_le();
0927             break;
0928         case PropertyType::Color:
0929             stream.read_uint32_le();
0930             break;
0931     }
0932 }
0933 
0934 const PropertyTable& RiveLoader::extra_properties() const
0935 {
0936     return extra_props;
0937 }
0938 
0939 QByteArray RiveLoader::read_raw_string()
0940 {
0941     auto size = stream.read_uint_leb128();
0942     if ( stream.has_error() )
0943         return {};
0944 
0945     return stream.read(size);
0946 }
0947 
0948 QString RiveLoader::read_string_utf8()
0949 {
0950     return QString::fromUtf8(read_raw_string());
0951 }