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 }