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

0001 /*
0002  * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best>
0003  *
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  */
0006 
0007 #pragma once
0008 
0009 #include "app/log/log.hpp"
0010 
0011 #include "model/shapes/group.hpp"
0012 #include "model/shapes/layer.hpp"
0013 #include "model/shapes/precomp_layer.hpp"
0014 #include "model/shapes/rect.hpp"
0015 #include "model/shapes/ellipse.hpp"
0016 #include "model/shapes/path.hpp"
0017 #include "model/shapes/polystar.hpp"
0018 #include "model/shapes/fill.hpp"
0019 #include "model/shapes/stroke.hpp"
0020 #include "model/shapes/image.hpp"
0021 #include "model/shapes/text.hpp"
0022 #include "model/shapes/repeater.hpp"
0023 
0024 #include "lottie_format.hpp"
0025 
0026 namespace glaxnimate::io::lottie::detail {
0027 
0028 class ValueTransform
0029 {
0030 public:
0031     virtual ~ValueTransform() {}
0032 
0033     virtual QVariant to_lottie(const QVariant& v, model::FrameTime) const = 0;
0034     virtual QVariant from_lottie(const QVariant& v, model::FrameTime) const = 0;
0035 };
0036 
0037 class FloatMult : public ValueTransform
0038 {
0039 public:
0040     explicit FloatMult(float factor) : factor(factor) {}
0041 
0042     QVariant to_lottie(const QVariant& v, model::FrameTime) const override
0043     {
0044         return v.toFloat() * factor;
0045     }
0046 
0047     QVariant from_lottie(const QVariant& v, model::FrameTime) const override
0048     {
0049         return v.toFloat() / factor;
0050     }
0051 private:
0052     float factor;
0053 };
0054 
0055 class EnumMap : public ValueTransform
0056 {
0057 public:
0058     EnumMap(QMap<int, int> values) : values(std::move(values)) {}
0059 
0060     QVariant to_lottie(const QVariant& v, model::FrameTime) const override
0061     {
0062         return values[v.toInt()];
0063     }
0064 
0065     QVariant from_lottie(const QVariant& v, model::FrameTime) const override
0066     {
0067         return values.key(v.toInt());
0068     }
0069 
0070     QMap<int, int> values;
0071 };
0072 
0073 class IntBool : public ValueTransform
0074 {
0075 public:
0076     QVariant to_lottie(const QVariant& v, model::FrameTime) const override
0077     {
0078         return int(v.toBool());
0079     }
0080 
0081     QVariant from_lottie(const QVariant& v, model::FrameTime) const override
0082     {
0083         return bool(v.toInt());
0084     }
0085 };
0086 
0087 class GradientLoad : public ValueTransform
0088 {
0089 public:
0090     GradientLoad(int count) : count(count) {}
0091 
0092     QVariant to_lottie(const QVariant&, model::FrameTime) const override { return {}; }
0093 
0094     QVariant from_lottie(const QVariant& v, model::FrameTime) const override
0095     {
0096         auto vlist = v.toList();
0097         if ( vlist.size() < count * 4 )
0098             return {};
0099 
0100         QGradientStops s;
0101         s.reserve(count);
0102         bool alpha = vlist.size() >= count * 6;
0103 
0104         for ( int i = 0; i < count; i++ )
0105         {
0106             s.push_back({
0107                 vlist[i*4].toDouble(),
0108                 QColor::fromRgbF(
0109                     vlist[i*4+1].toDouble(),
0110                     vlist[i*4+2].toDouble(),
0111                     vlist[i*4+3].toDouble(),
0112                     alpha ? vlist[count*4+2*i+1].toDouble() : 1
0113                 )
0114             });
0115         }
0116 
0117         return QVariant::fromValue(s);
0118     }
0119 
0120     int count = 0;
0121 };
0122 
0123 
0124 class TransformFunc
0125 {
0126 public:
0127     template<class T, class = std::enable_if_t<std::is_base_of_v<ValueTransform, T>>>
0128     TransformFunc(const T& t) : trans(std::make_shared<T>(t)) {}
0129 
0130     TransformFunc() {}
0131     TransformFunc(TransformFunc&&) = default;
0132     TransformFunc(const TransformFunc&) = default;
0133 
0134     QVariant to_lottie(const QVariant& v, model::FrameTime t) const
0135     {
0136         if ( !trans )
0137             return v;
0138         return trans->to_lottie(v, t);
0139     }
0140 
0141     QVariant from_lottie(const QVariant& v, model::FrameTime t) const
0142     {
0143         if ( !trans )
0144             return v;
0145         return trans->from_lottie(v, t);
0146     }
0147 
0148 private:
0149     std::shared_ptr<ValueTransform> trans;
0150 };
0151 
0152 enum FieldMode
0153 {
0154     Auto,
0155     AnimatedToStatic,
0156     Ignored,
0157     Custom
0158 };
0159 
0160 struct FieldInfo
0161 {
0162     QString name;
0163     QString lottie;
0164     bool essential;
0165     FieldMode mode;
0166     TransformFunc transform;
0167 
0168     FieldInfo(const char* lottie, const char* name, TransformFunc transform = {}, bool essential = true)
0169         : name(name), lottie(lottie), essential(essential), mode(Auto), transform(std::move(transform))
0170     {}
0171 
0172     FieldInfo(const char* lottie, FieldMode mode = Ignored)
0173         : lottie(lottie), essential(false), mode(mode)
0174     {}
0175 
0176     FieldInfo(const char* lottie, const char* name, FieldMode mode, bool essential = true)
0177         : name(name), lottie(lottie), essential(essential), mode(mode)
0178     {}
0179 };
0180 
0181 // static mapping data
0182 const QMap<QString, QVector<FieldInfo>> fields = {
0183     {"DocumentNode", {
0184         FieldInfo{"nm", "name", {}, false},
0185         FieldInfo{"mn", "uuid", {}, false},
0186     }},
0187     {"Composition", {
0188         FieldInfo("layers", Custom),
0189         FieldInfo("id", Custom),
0190         FieldInfo{"op", Custom},
0191         FieldInfo{"ip", Custom},
0192         FieldInfo("v", Custom),
0193         FieldInfo{"fr", "fps"},
0194         FieldInfo{"w", "width"},
0195         FieldInfo{"h", "height"},
0196         FieldInfo("ddd"),
0197         FieldInfo("assets"),
0198         FieldInfo("comps"),
0199         FieldInfo("fonts"),
0200         FieldInfo("chars"),
0201         FieldInfo("markers"),
0202         FieldInfo("motion_blur"),
0203         FieldInfo("tgs"),
0204         FieldInfo("meta", Custom),
0205         FieldInfo("props"),
0206         FieldInfo("slots"),
0207     }},
0208     // Layer is converted explicitly
0209     {"__Layer__", {
0210         FieldInfo{"op", Custom},
0211         FieldInfo{"ip", Custom},
0212         FieldInfo("ddd"),
0213         FieldInfo("hd", Custom),
0214         FieldInfo("ty", Custom),
0215         FieldInfo("parent", Custom),
0216         FieldInfo("sr"),
0217         FieldInfo("ks", Custom),
0218         FieldInfo("ao", "auto_orient", IntBool()),
0219         FieldInfo{"st", Custom},
0220         FieldInfo("bm"),
0221         FieldInfo("tt"),
0222         FieldInfo("td"),
0223         FieldInfo("ind", Custom),
0224         FieldInfo("cl"),
0225         FieldInfo("ln"),
0226         FieldInfo("hasMasks", Custom),
0227         FieldInfo("masksProperties", Custom),
0228         FieldInfo("ef"),
0229         FieldInfo("bounds"), // old, no longer there
0230         FieldInfo("ct"),
0231     }},
0232     {"Transform", {
0233         FieldInfo{"a", "anchor_point"},
0234         FieldInfo("px", Custom),
0235         FieldInfo("py", Custom),
0236         FieldInfo("pz", Custom),
0237         FieldInfo{"p", "position"},
0238         FieldInfo{"s", "scale"},
0239         FieldInfo{"r", "rotation"},
0240         FieldInfo("rx", Custom),
0241         FieldInfo("ry", Custom),
0242         FieldInfo("rz", Custom),
0243         FieldInfo("o"),
0244         FieldInfo("sk"),
0245         FieldInfo("sa"),
0246         FieldInfo("nm"),
0247     }},
0248     {"ShapeElement", {
0249         FieldInfo{"ty", Custom},
0250         FieldInfo{"ix"},
0251         FieldInfo{"cix"},
0252         FieldInfo{"bm"},
0253         FieldInfo{"hd", Custom},
0254     }},
0255     {"Shape", {
0256         FieldInfo{"d", "reversed", EnumMap{{
0257             {1, 3},
0258             {0, 1},
0259         }}},
0260         FieldInfo{"closed", Custom}, // Old attribute
0261     }},
0262     {"Rect", {
0263         FieldInfo{"p", "position"},
0264         FieldInfo{"s", "size"},
0265         FieldInfo{"r", "rounded"},
0266     }},
0267     {"Ellipse", {
0268         FieldInfo{"p", "position"},
0269         FieldInfo{"s", "size"},
0270     }},
0271     {"Path", {
0272         FieldInfo{"ks", "shape"},
0273         FieldInfo{"ind"},
0274     }},
0275     {"PolyStar", {
0276         FieldInfo{"p", "position"},
0277         FieldInfo{"or", "outer_radius"},
0278         FieldInfo{"ir", "inner_radius"},
0279         FieldInfo{"is", "inner_roundness", FloatMult(100)},
0280         FieldInfo{"os", "outer_roundness", FloatMult(100)},
0281         FieldInfo{"r", "angle"},
0282         FieldInfo{"pt", "points"},
0283         FieldInfo{"sy", "type"},
0284     }},
0285     {"Group", {
0286         FieldInfo{"np"},
0287         FieldInfo{"it", Custom},
0288     }},
0289     {"Styler", {
0290         FieldInfo{"o", "opacity", FloatMult(100)},
0291         FieldInfo{"c", Custom},
0292         FieldInfo{"fillEnabled", Custom},
0293     }},
0294     {"Fill", {
0295         FieldInfo{"r", "fill_rule", EnumMap{{
0296             {model::Fill::NonZero, 1},
0297             {model::Fill::EvenOdd, 2},
0298         }}},
0299     }},
0300     {"Stroke", {
0301         FieldInfo{"lc", "cap", EnumMap{{
0302             {model::Stroke::ButtCap,   1},
0303             {model::Stroke::RoundCap,  2},
0304             {model::Stroke::SquareCap, 3},
0305         }}},
0306         FieldInfo{"lj", "join", EnumMap{{
0307             {model::Stroke::MiterJoin, 1},
0308             {model::Stroke::RoundJoin, 2},
0309             {model::Stroke::BevelJoin, 3},
0310         }}},
0311         FieldInfo{"ml", "miter_limit"},
0312         FieldInfo{"w", "width"},
0313         FieldInfo{"d"},
0314     }},
0315     {"Bitmap", {
0316         FieldInfo{"h", "height"},
0317         FieldInfo{"w", "width"},
0318         FieldInfo{"id", Custom},
0319         FieldInfo{"p", Custom},
0320         FieldInfo{"u", Custom},
0321         FieldInfo{"e", Custom},
0322     }},
0323     {"Gradient", {
0324         FieldInfo{"s", "start_point"},
0325         FieldInfo{"e", "end_point"},
0326         FieldInfo{"t", "type"},
0327         FieldInfo{"h", Custom}, /// \todo
0328         FieldInfo{"a", Custom}, /// \todo
0329         FieldInfo{"g", Custom},
0330     }},
0331     {"PreCompLayer", {
0332         FieldInfo{"refId", Custom},
0333         FieldInfo{"w", Custom},
0334         FieldInfo{"h", Custom},
0335         FieldInfo{"tm"},
0336     }},
0337     {"Repeater", {
0338         FieldInfo{"c", "copies"},
0339         FieldInfo{"o"},
0340         FieldInfo{"m"},
0341         FieldInfo{"tr", Custom},
0342     }},
0343     {"Trim", {
0344         FieldInfo{"s", "start", FloatMult(100)},
0345         FieldInfo{"e", "end", FloatMult(100)},
0346         FieldInfo{"o", "offset", FloatMult(360)},
0347         FieldInfo{"m", "multiple"},
0348     }},
0349     {"InflateDeflate", {
0350         FieldInfo{"a", "amount", FloatMult(100)},
0351     }},
0352     {"RoundCorners", {
0353         FieldInfo{"r", "radius"},
0354     }},
0355     {"OffsetPath", {
0356         FieldInfo{"a", "amount"},
0357         FieldInfo{"lj", "join", EnumMap{{
0358             {model::Stroke::MiterJoin, 1},
0359             {model::Stroke::RoundJoin, 2},
0360             {model::Stroke::BevelJoin, 3},
0361         }}},
0362         FieldInfo{"ml", "miter_limit"},
0363     }},
0364     {"ZigZag", {
0365         FieldInfo{"s", "amplitude"},
0366         FieldInfo{"r", "frequency"},
0367         FieldInfo{"pt", "style", AnimatedToStatic},
0368     }},
0369 };
0370 const QMap<QString, QString> shape_types = {
0371     {"Rect", "rc"},
0372     {"PolyStar", "sr"},
0373     {"Ellipse", "el"},
0374     {"Path", "sh"},
0375     {"Group", "gr"},
0376     {"Layer", "gr"},
0377     {"Fill", "fl"},
0378     {"Stroke", "st"},
0379     // "gf" (Gradient Fill) and "gs" (Gradient Stroke) are handled by fill/stroke
0380     // "tr" is not a shape but a property of groups
0381     // "mm" (Merge), "tw" (Twist), are not supported by lottie
0382     {"Trim", "tm"},
0383     {"Repeater", "rp"},
0384     {"RoundCorners", "rd"},
0385     {"InflateDeflate", "pb"},
0386     {"OffsetPath", "op"},
0387     {"ZigZag", "zz"},
0388 };
0389 
0390 const QMap<QString, QString> shape_types_repeat = {
0391     {"gf", "Fill"},
0392     {"gs", "Stroke"},
0393 };
0394 
0395 const QMap<int, QString> unsupported_layers = {
0396     {6, "Audio"},
0397     {7, "Pholder Video"},
0398     {8, "Image Sequence"},
0399     {9, "Video"},
0400 };
0401 
0402 } // namespace glaxnimate::io::lottie::detail