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

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_format.hpp"
0008 
0009 #include <QJsonArray>
0010 #include <QJsonObject>
0011 
0012 
0013 #include "rive_loader.hpp"
0014 #include "rive_exporter.hpp"
0015 
0016 glaxnimate::io::Autoreg<glaxnimate::io::rive::RiveFormat> glaxnimate::io::rive::RiveFormat::autoreg;
0017 
0018 
0019 bool glaxnimate::io::rive::RiveFormat::on_open(QIODevice& file, const QString&, model::Document* document, const QVariantMap&)
0020 {
0021     BinaryInputStream stream(&file);
0022     if ( stream.read(4) != "RIVE" )
0023     {
0024         error(i18n("Unsupported format"));
0025         return false;
0026     }
0027 
0028     auto vmaj = stream.read_uint_leb128();
0029     auto vmin = stream.read_uint_leb128();
0030     stream.read_uint_leb128(); // file id
0031 
0032     if ( stream.has_error() )
0033     {
0034         error(i18n("Could not read header"));
0035         return false;
0036     }
0037 
0038     if ( vmaj != RiveFormat::format_version )
0039     {
0040         error(i18n("Loading unsupported rive file version %1.%2, the only supported version is %3", vmaj, vmin, 7));
0041         return false;
0042     }
0043 
0044     if ( stream.has_error() )
0045     {
0046         error(i18n("Could not read property table"));
0047         return false;
0048     }
0049 
0050     return RiveLoader(stream, this).load_document(document);
0051 }
0052 
0053 bool glaxnimate::io::rive::RiveFormat::on_save(QIODevice& device, const QString&, model::Composition* comp, const QVariantMap&)
0054 {
0055     RiveExporter exporter(&device, this);
0056     exporter.write_document(comp->document());
0057     return true;
0058 }
0059 
0060 static QString property_type_to_string(glaxnimate::io::rive::PropertyType type)
0061 {
0062     switch ( type )
0063     {
0064         case glaxnimate::io::rive::PropertyType::VarUint:
0065             return "VarUint";
0066         case glaxnimate::io::rive::PropertyType::Bool:
0067             return "bool";
0068         case glaxnimate::io::rive::PropertyType::String:
0069             return "string";
0070         case glaxnimate::io::rive::PropertyType::Bytes:
0071             return "bytes";
0072         case glaxnimate::io::rive::PropertyType::Float:
0073             return "float";
0074         case glaxnimate::io::rive::PropertyType::Color:
0075             return "color";
0076     }
0077     return "?";
0078 }
0079 
0080 QJsonDocument glaxnimate::io::rive::RiveFormat::to_json(const QByteArray& binary_data)
0081 {
0082     BinaryInputStream stream(binary_data);
0083     if ( stream.read(4) != "RIVE" )
0084         return {};
0085 
0086     auto vmaj = stream.read_uint_leb128();
0087     auto vmin = stream.read_uint_leb128();
0088     auto file_id = stream.read_uint_leb128();
0089 
0090     if ( stream.has_error() || vmaj != RiveFormat::format_version )
0091         return {};
0092 
0093     RiveLoader loader(stream, this);
0094 
0095     QJsonArray summary;
0096 
0097     QJsonArray objects;
0098     int id = 0;
0099     bool has_artboard = false;
0100     for ( const auto& rive_obj : loader.load_object_list() )
0101     {
0102         if ( !rive_obj )
0103         {
0104             summary.push_back("Invalid");
0105             objects.push_back("Invalid");
0106             continue;
0107         }
0108 
0109         if ( rive_obj.type().id == TypeId::Artboard )
0110         {
0111             has_artboard = true;
0112             id = 0;
0113         }
0114 
0115         QJsonObject summary_obj;
0116         QJsonObject obj;
0117 
0118         QJsonArray types;
0119         for ( const auto& def : rive_obj.type().definitions )
0120         {
0121             QJsonObject jdef;
0122             jdef["id"] = int(def->type_id);
0123             jdef["name"] = def->name;
0124             types.push_back(jdef);
0125         }
0126         obj["class"] = types;
0127 
0128         QJsonArray props;
0129         for ( const auto& p : rive_obj.type().properties )
0130         {
0131             QJsonObject prop;
0132             prop["id"] = int(p->id);
0133             prop["name"] = p->name;
0134             prop["type"] = property_type_to_string(p->type);
0135             auto iter = rive_obj.properties().find(p);
0136             QJsonValue val;
0137 
0138             if ( iter != rive_obj.properties().end() && iter->second.isValid() )
0139             {
0140                 if ( iter->second.userType() == QMetaType::QColor )
0141                     val = iter->second.value<QColor>().name();
0142                 else if ( iter->second.userType() == QMetaType::ULongLong || iter->second.userType() == QMetaType::ULong )
0143                     val = iter->second.toInt();
0144                 else if ( iter->second.userType() == QMetaType::QByteArray )
0145                     val = QString::fromLatin1(iter->second.toByteArray().toBase64());
0146                 else
0147                     val = QJsonValue::fromVariant(iter->second);
0148 
0149                 summary_obj[iter->first->name] = val;
0150             }
0151             prop["value"] = val;
0152 
0153             props.push_back(prop);
0154         }
0155         obj["properties"] = props;
0156 
0157         QJsonObject summary_obj_parent;
0158         summary_obj_parent[!rive_obj ? "?" : rive_obj.definition()->name] = summary_obj;
0159 
0160         if ( has_artboard )
0161         {
0162             summary_obj_parent["-id"] = id;
0163             obj["object_id"] = id;
0164             id++;
0165         }
0166 
0167         objects.push_back(obj);
0168         summary.push_back(summary_obj_parent);
0169     }
0170 
0171     QJsonObject header;
0172     QJsonArray version;
0173     version.push_back(int(vmaj));
0174     version.push_back(int(vmin));
0175     header["version"] = version;
0176     header["file_id"] = int(file_id);
0177     QJsonArray extra_props;
0178     for ( const auto& p : loader.extra_properties() )
0179     {
0180         QJsonObject prop;
0181         prop["id"] = int(p.first);
0182         prop["type"] = property_type_to_string(p.second);
0183     }
0184     header["toc"] = extra_props;
0185 
0186     QJsonObject root;
0187     root["brief"] = summary;
0188     root["detail"] = objects;
0189     root["header"] = header;
0190 
0191     return QJsonDocument(root);
0192 }