Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2021 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 : #include "drawdatajson.h"
23 :
24 : #include "global/serialization/json.h"
25 :
26 : #include "log.h"
27 :
28 : using namespace muse;
29 : using namespace muse::draw;
30 :
31 5071 : static int rtoi(double v)
32 : {
33 5071 : return static_cast<int>(v * 1000.0);
34 : }
35 :
36 26 : static double itor(int v)
37 : {
38 26 : return static_cast<double>(v) / 1000.0;
39 : }
40 :
41 409 : static JsonArray toArr(const std::vector<double>& vv)
42 : {
43 409 : JsonArray a;
44 409 : for (double v : vv) {
45 0 : a.append(rtoi(v));
46 : }
47 409 : return a;
48 0 : }
49 :
50 4 : static void fromArr(const JsonArray& arr, std::vector<double>& vv)
51 : {
52 4 : for (size_t i = 0; i < arr.size(); ++i) {
53 0 : vv.push_back(itor(arr.at(i).toDouble()));
54 : }
55 4 : }
56 :
57 409 : static JsonObject toObj(const Pen& pen)
58 : {
59 409 : JsonObject obj;
60 818 : obj["style"] = static_cast<int>(pen.style());
61 818 : obj["capStyle"] = static_cast<int>(pen.capStyle());
62 818 : obj["joinStyle"] = static_cast<int>(pen.joinStyle());
63 818 : obj["miterLimit"] = pen.miterLimit();
64 1227 : obj["color"] = pen.color().toString();
65 818 : obj["width"] = pen.widthF();
66 1227 : obj["dashPattern"] = toArr(pen.dashPattern());
67 409 : return obj;
68 0 : }
69 :
70 4 : static void fromObj(const JsonObject& obj, Pen& pen)
71 : {
72 12 : pen.setStyle(static_cast<PenStyle>(obj.value("style").toInt()));
73 12 : pen.setCapStyle(static_cast<PenCapStyle>(obj.value("capStyle").toInt()));
74 12 : pen.setJoinStyle(static_cast<PenJoinStyle>(obj.value("joinStyle").toInt()));
75 8 : if (obj.contains("miterLimit")) {
76 6 : pen.setMiterLimit(obj.value("miterLimit").toDouble());
77 : }
78 12 : pen.setColor(Color(obj.value("color").toStdString().c_str()));
79 12 : pen.setWidthF(obj.value("width").toDouble());
80 :
81 4 : std::vector<double> dp;
82 12 : fromArr(obj.value("dashPattern").toArray(), dp);
83 4 : pen.setDashPattern(dp);
84 4 : }
85 :
86 409 : static JsonObject toObj(const Brush& brush)
87 : {
88 409 : JsonObject obj;
89 818 : obj["style"] = static_cast<int>(brush.style());
90 1227 : obj["color"] = brush.color().toString();
91 409 : return obj;
92 0 : }
93 :
94 4 : static void fromObj(const JsonObject& obj, Brush& brush)
95 : {
96 8 : brush.setStyle(static_cast<BrushStyle>(obj["style"].toInt()));
97 4 : brush.setColor(Color(obj["color"].toStdString().c_str()));
98 4 : }
99 :
100 401 : static JsonObject toObj(const Font& font)
101 : {
102 401 : JsonObject obj;
103 1203 : obj["family"] = font.family().id();
104 802 : obj["type"] = static_cast<int>(font.type());
105 802 : obj["pointSize"] = font.pointSizeF();
106 802 : obj["weight"] = font.weight();
107 802 : obj["bold"] = font.bold();
108 802 : obj["italic"] = font.italic();
109 802 : obj["underline"] = font.underline();
110 802 : obj["strike"] = font.strike();
111 802 : obj["hinting"] = static_cast<int>(font.hinting());
112 802 : obj["no_merging"] = font.noFontMerging();
113 401 : return obj;
114 0 : }
115 :
116 2 : static void fromObj(const JsonObject& obj, Font& font)
117 : {
118 10 : font.setFamily(obj.value("family").toString(), static_cast<Font::Type>(obj.value("type").toInt()));
119 6 : font.setPointSizeF(obj.value("pointSize").toDouble());
120 6 : font.setWeight(static_cast<Font::Weight>(obj.value("weight").toInt()));
121 6 : font.setBold(obj.value("bold").toBool());
122 6 : font.setItalic(obj.value("italic").toBool());
123 6 : font.setUnderline(obj.value("underline").toBool());
124 6 : font.setStrike(obj.value("strike").toBool());
125 6 : font.setHinting(static_cast<Font::Hinting>(obj.value("hinting").toInt()));
126 6 : font.setNoFontMerging(obj.value("no_merging").toBool());
127 2 : }
128 :
129 401 : static JsonArray toArr(const Transform& t)
130 : {
131 : return JsonArray({ rtoi(t.m11()), rtoi(t.m12()), rtoi(t.m13()),
132 : rtoi(t.m21()), rtoi(t.m22()), rtoi(t.m23()),
133 4010 : rtoi(t.m31()), rtoi(t.m32()), rtoi(t.m33()) });
134 401 : }
135 :
136 2 : static void fromArr(const JsonArray& arr, Transform& t)
137 : {
138 2 : IF_ASSERT_FAILED(arr.size() == 9) {
139 0 : return;
140 : }
141 :
142 12 : t.setMatrix(itor(arr.at(0).toInt()), itor(arr.at(1).toInt()), itor(arr.at(2).toInt()),
143 8 : itor(arr.at(3).toInt()), itor(arr.at(4).toInt()), itor(arr.at(5).toInt()),
144 8 : itor(arr.at(6).toInt()), itor(arr.at(7).toInt()), itor(arr.at(8).toInt()));
145 : }
146 :
147 677 : static JsonArray toArr(const PointF& p)
148 : {
149 2031 : return JsonArray({ rtoi(p.x()), rtoi(p.y()) });
150 677 : }
151 :
152 0 : static void fromArr(const JsonArray& arr, PointF& p)
153 : {
154 0 : IF_ASSERT_FAILED(arr.size() == 2) {
155 0 : return;
156 : }
157 0 : p.setX(itor(arr.at(0).toInt()));
158 0 : p.setY(itor(arr.at(1).toInt()));
159 : }
160 :
161 12 : static JsonArray toArr(const RectF& r)
162 : {
163 60 : return JsonArray({ rtoi(r.x()), rtoi(r.y()), rtoi(r.width()), rtoi(r.height()) });
164 12 : }
165 :
166 2 : static void fromArr(const JsonArray& arr, RectF& r)
167 : {
168 2 : IF_ASSERT_FAILED(arr.size() == 4) {
169 0 : return;
170 : }
171 2 : r = RectF(itor(arr.at(0).toInt()), itor(arr.at(1).toInt()), itor(arr.at(2).toInt()), itor(arr.at(3).toInt()));
172 : }
173 :
174 0 : static JsonArray toArr(const Size& sz)
175 : {
176 0 : return JsonArray({ sz.width(), sz.height() });
177 0 : }
178 :
179 0 : static void fromArr(const JsonArray& arr, Size& sz)
180 : {
181 0 : IF_ASSERT_FAILED(arr.size() == 2) {
182 0 : return;
183 : }
184 0 : sz = Size(arr.at(0).toInt(), arr.at(1).toInt());
185 : }
186 :
187 401 : static JsonObject toObj(const DrawData::State& st)
188 : {
189 401 : JsonObject obj;
190 1203 : obj["pen"] = toObj(st.pen);
191 1203 : obj["brush"] = toObj(st.brush);
192 1203 : obj["font"] = toObj(st.font);
193 802 : obj["isAntialiasing"] = st.isAntialiasing;
194 1203 : obj["transform"] = toArr(st.transform);
195 802 : obj["compositionMode"] = static_cast<int>(st.compositionMode);
196 401 : return obj;
197 0 : }
198 :
199 2 : static void fromObj(const JsonObject& obj, DrawData::State& st)
200 : {
201 4 : fromObj(obj["pen"].toObject(), st.pen);
202 4 : fromObj(obj["brush"].toObject(), st.brush);
203 6 : fromObj(obj["font"].toObject(), st.font);
204 2 : st.isAntialiasing = obj["isAntialiasing"].toBool();
205 6 : fromArr(obj["transform"].toArray(), st.transform);
206 2 : st.compositionMode = static_cast<CompositionMode>(obj["compositionMode"].toInt());
207 2 : }
208 :
209 8 : static JsonObject toObj(const PainterPath& path)
210 : {
211 8 : JsonObject obj;
212 16 : obj["fillRule"] = static_cast<int>(path.fillRule());
213 :
214 8 : JsonArray elsArr;
215 38 : for (size_t i = 0; i < path.elementCount(); ++i) {
216 30 : PainterPath::Element e = path.elementAt(i);
217 120 : elsArr.append(JsonArray({ static_cast<int>(e.type), rtoi(e.x), rtoi(e.y) }));
218 : }
219 8 : obj["elements"] = elsArr;
220 8 : return obj;
221 38 : }
222 :
223 8 : static JsonObject toObj(const DrawPath& path)
224 : {
225 8 : JsonObject obj;
226 24 : obj["path"] = toObj(path.path);
227 24 : obj["pen"] = toObj(path.pen);
228 24 : obj["brush"] = toObj(path.brush);
229 16 : obj["mode"] = static_cast<int>(path.mode);
230 8 : return obj;
231 0 : }
232 :
233 2 : static void fromObj(const JsonObject& obj, PainterPath& path)
234 : {
235 4 : path.setFillRule(static_cast<PainterPath::FillRule>(obj["fillRule"].toInt()));
236 :
237 2 : JsonArray elsArr = obj["elements"].toArray();
238 2 : std::vector<PainterPath::Element> curveEls;
239 2 : for (size_t i = 0; i < elsArr.size(); ++i) {
240 0 : JsonArray elArr = elsArr.at(i).toArray();
241 0 : IF_ASSERT_FAILED(elArr.size() == 3) {
242 0 : continue;
243 : }
244 :
245 0 : PainterPath::ElementType type = static_cast<PainterPath::ElementType>(elArr.at(0).toInt());
246 0 : double x = itor(elArr.at(1).toInt());
247 0 : double y = itor(elArr.at(2).toInt());
248 :
249 0 : switch (type) {
250 0 : case PainterPath::ElementType::MoveToElement: {
251 0 : path.moveTo(x, y);
252 0 : } break;
253 0 : case PainterPath::ElementType::LineToElement: {
254 0 : path.lineTo(x, y);
255 0 : } break;
256 0 : case PainterPath::ElementType::CurveToElement: {
257 0 : IF_ASSERT_FAILED(curveEls.empty()) {
258 0 : continue;
259 : }
260 0 : PainterPath::Element e(x, y, type);
261 0 : curveEls.push_back(std::move(e));
262 0 : } break;
263 0 : case PainterPath::ElementType::CurveToDataElement: {
264 0 : if (curveEls.size() == 1) { // only CurveToElement
265 0 : PainterPath::Element e(x, y, type);
266 0 : curveEls.push_back(std::move(e));
267 0 : continue;
268 0 : }
269 :
270 0 : IF_ASSERT_FAILED(curveEls.size() == 2) { // must be CurveToElement and one CurveToDataElement
271 0 : curveEls.clear();
272 0 : continue;
273 : }
274 :
275 0 : path.cubicTo(curveEls.at(0).x, curveEls.at(0).y, curveEls.at(1).x, curveEls.at(1).y, x, y);
276 0 : curveEls.clear();
277 0 : } break;
278 : }
279 0 : }
280 2 : }
281 :
282 2 : static void fromObj(const JsonObject& obj, DrawPath& path)
283 : {
284 4 : fromObj(obj["path"].toObject(), path.path);
285 4 : fromObj(obj["pen"].toObject(), path.pen);
286 6 : fromObj(obj["brush"].toObject(), path.brush);
287 2 : path.mode = static_cast<DrawMode>(obj["mode"].toInt());
288 2 : }
289 :
290 223 : static JsonArray toArr(const PolygonF& pol)
291 : {
292 223 : JsonArray a;
293 669 : for (const PointF& p : pol) {
294 446 : a.append(toArr(p));
295 : }
296 223 : return a;
297 0 : }
298 :
299 0 : static void fromArr(const JsonArray& arr, PolygonF& pol)
300 : {
301 0 : pol.reserve(arr.size());
302 0 : for (size_t i = 0; i < arr.size(); ++i) {
303 0 : PointF p;
304 0 : fromArr(arr.at(i).toArray(), p);
305 0 : pol.push_back(std::move(p));
306 : }
307 0 : }
308 :
309 223 : static JsonObject toObj(const DrawPolygon& pol)
310 : {
311 223 : JsonObject obj;
312 669 : obj["polygon"] = toArr(pol.polygon);
313 446 : obj["mode"] = static_cast<int>(pol.mode);
314 223 : return obj;
315 0 : }
316 :
317 0 : static void fromObj(const JsonObject& obj, DrawPolygon& pol)
318 : {
319 0 : fromArr(obj["polygon"].toArray(), pol.polygon);
320 0 : pol.mode = static_cast<PolygonMode>(obj["mode"].toInt());
321 0 : }
322 :
323 231 : static JsonObject toObj(const DrawText& text)
324 : {
325 231 : JsonObject o;
326 231 : if (text.mode == DrawText::Point) {
327 693 : o["point"] = toArr(text.rect.topLeft());
328 : } else {
329 0 : o["rect"] = toArr(text.rect);
330 : }
331 462 : o["alignment"] = static_cast<int>(text.alignment);
332 462 : o["textFlags"] = static_cast<int>(text.textFlags);
333 462 : o["text"] = text.text;
334 231 : return o;
335 0 : }
336 :
337 0 : static void fromObj(const JsonObject& obj, DrawText& text)
338 : {
339 0 : if (obj.contains("point")) {
340 0 : PointF point;
341 0 : fromArr(obj["point"].toArray(), point);
342 0 : text.mode = DrawText::Point;
343 0 : text.rect = RectF(point, SizeF());
344 : } else {
345 0 : fromArr(obj["rect"].toArray(), text.rect);
346 0 : text.mode = DrawText::Rect;
347 : }
348 0 : text.alignment = static_cast<Alignment>(obj["alignment"].toInt());
349 0 : text.textFlags = static_cast<TextFlags>(obj["textFlags"].toInt());
350 0 : text.text = obj["text"].toString();
351 0 : }
352 :
353 0 : static JsonObject toObj(const DrawPixmap& pm)
354 : {
355 0 : JsonObject o;
356 0 : if (pm.mode == DrawPixmap::Single) {
357 0 : o["point"] = toArr(pm.rect.topLeft());
358 : } else {
359 0 : o["rect"] = toArr(pm.rect);
360 0 : o["offset"] = toArr(pm.offset);
361 : }
362 0 : o["pmSize"] = toArr(pm.pm.size());
363 0 : return o;
364 0 : }
365 :
366 0 : static void fromObj(const JsonObject& obj, DrawPixmap& pm)
367 : {
368 0 : if (obj.contains("point")) {
369 0 : pm.mode = DrawPixmap::Single;
370 0 : PointF point;
371 0 : fromArr(obj["point"].toArray(), point);
372 0 : pm.rect = RectF(point, SizeF());
373 : } else {
374 0 : pm.mode = DrawPixmap::Tiled;
375 0 : fromArr(obj["rect"].toArray(), pm.rect);
376 0 : fromArr(obj["offset"].toArray(), pm.offset);
377 : }
378 :
379 0 : Size size;
380 0 : fromArr(obj["pmSize"].toArray(), size);
381 0 : pm.pm = Pixmap(size);
382 0 : }
383 :
384 : template<class T>
385 380 : static JsonArray toArr(const std::vector<T>& vals)
386 : {
387 380 : JsonArray arr;
388 842 : for (size_t i = 0; i < vals.size(); ++i) {
389 462 : arr << toObj(vals.at(i));
390 : }
391 380 : return arr;
392 0 : }
393 :
394 : template<class T>
395 2 : static void fromArr(const JsonArray& arr, std::vector<T>& vals)
396 : {
397 2 : vals.reserve(arr.size());
398 4 : for (size_t i = 0; i < arr.size(); ++i) {
399 2 : T val;
400 2 : fromObj(arr.at(i).toObject(), val);
401 2 : vals.push_back(std::move(val));
402 : }
403 2 : }
404 :
405 23 : static JsonObject toObj(const DrawData::Item& obj)
406 : {
407 : //! NOTE 'a' added to the beginning of some field names for convenient sorting
408 :
409 23 : JsonObject objObj;
410 46 : objObj["a_name"] = obj.name;
411 :
412 23 : JsonArray childrenArr;
413 34 : for (const DrawData::Item& ch : obj.chilren) {
414 11 : childrenArr.append(toObj(ch));
415 : }
416 23 : objObj["children"] = childrenArr;
417 :
418 23 : JsonArray datasArr;
419 403 : for (const DrawData::Data& data : obj.datas) {
420 380 : if (data.empty()) {
421 0 : continue;
422 : }
423 :
424 380 : JsonObject dataObj;
425 760 : dataObj["state"] = data.state;
426 380 : if (!data.paths.empty()) {
427 24 : dataObj["paths"] = toArr(data.paths);
428 : }
429 380 : if (!data.polygons.empty()) {
430 423 : dataObj["polygons"] = toArr(data.polygons);
431 : }
432 380 : if (!data.texts.empty()) {
433 693 : dataObj["texts"] = toArr(data.texts);
434 : }
435 380 : if (!data.pixmaps.empty()) {
436 0 : dataObj["pixmaps"] = toArr(data.pixmaps);
437 : }
438 :
439 380 : datasArr.append(dataObj);
440 380 : }
441 23 : objObj["datas"] = datasArr;
442 :
443 23 : return objObj;
444 23 : }
445 :
446 2 : static void fromObj(const JsonObject& objObj, DrawData::Item& obj)
447 : {
448 6 : obj.name = objObj.value("a_name").toString().toStdString();
449 2 : JsonArray childrenArr = objObj["children"].toArray();
450 2 : for (size_t j = 0; j < childrenArr.size(); ++j) {
451 0 : DrawData::Item& ch = obj.chilren.emplace_back();
452 0 : const JsonObject childObj = childrenArr.at(j).toObject();
453 0 : fromObj(childObj, ch);
454 0 : }
455 :
456 2 : JsonArray datasArr = objObj["datas"].toArray();
457 4 : for (size_t j = 0; j < datasArr.size(); ++j) {
458 2 : const JsonObject dataObj = datasArr.at(j).toObject();
459 2 : DrawData::Data data;
460 4 : data.state = dataObj["state"].toInt();
461 4 : if (dataObj.contains("paths")) {
462 6 : fromArr(dataObj.value("paths").toArray(), data.paths);
463 : }
464 4 : if (dataObj.contains("polygons")) {
465 0 : fromArr(dataObj.value("polygons").toArray(), data.polygons);
466 : }
467 4 : if (dataObj.contains("texts")) {
468 0 : fromArr(dataObj.value("texts").toArray(), data.texts);
469 : }
470 4 : if (dataObj.contains("pixmaps")) {
471 0 : fromArr(dataObj.value("pixmaps").toArray(), data.pixmaps);
472 : }
473 :
474 2 : obj.datas.push_back(std::move(data));
475 2 : }
476 2 : }
477 :
478 12 : void DrawDataJson::toJson(JsonObject& root, const DrawDataPtr& dd)
479 : {
480 : //! NOTE 'a' added to the beginning of some field names for convenient sorting
481 :
482 24 : root["a_name"] = dd->name;
483 36 : root["a_viewport"] = toArr(dd->viewport);
484 :
485 : // item
486 36 : root["item"] = toObj(dd->item);
487 :
488 : // states
489 12 : JsonObject stateObj;
490 413 : for (auto it = dd->states.cbegin(); it != dd->states.cend(); ++it) {
491 401 : stateObj[std::to_string(it->first)] = toObj(it->second);
492 : }
493 12 : root["states"] = stateObj;
494 12 : }
495 :
496 2 : void DrawDataJson::fromJson(const JsonObject& root, DrawDataPtr& dd)
497 : {
498 : // read states
499 : {
500 6 : const JsonObject obj = root.value("states").toObject();
501 2 : std::vector<std::string> keys = obj.keys();
502 4 : for (const std::string& k : keys) {
503 2 : DrawData::State state;
504 2 : fromObj(obj.value(k).toObject(), state);
505 2 : dd->states[std::stoi(k)] = state;
506 2 : }
507 2 : }
508 :
509 6 : dd->name = root.value("a_name").toStdString();
510 6 : fromArr(root.value("a_viewport").toArray(), dd->viewport);
511 :
512 6 : JsonObject itemObj = root.value("item").toObject();
513 2 : fromObj(itemObj, dd->item);
514 2 : }
515 :
516 12 : ByteArray DrawDataJson::toJson(const DrawDataPtr& data, bool prettify)
517 : {
518 12 : IF_ASSERT_FAILED(data) {
519 0 : return ByteArray();
520 : }
521 :
522 12 : JsonObject root;
523 12 : toJson(root, data);
524 12 : return JsonDocument(root).toJson(prettify ? JsonDocument::Format::Indented : JsonDocument::Format::Compact);
525 12 : }
526 :
527 2 : RetVal<DrawDataPtr> DrawDataJson::fromJson(const ByteArray& json)
528 : {
529 2 : std::string err;
530 2 : JsonDocument doc = JsonDocument::fromJson(json, &err);
531 2 : if (!err.empty()) {
532 0 : RetVal<DrawDataPtr> rv;
533 0 : rv.ret = make_ret(Ret::Code::UnknownError, err);
534 0 : return rv;
535 0 : }
536 :
537 2 : const JsonObject root = doc.rootObject();
538 2 : DrawDataPtr dd = std::make_shared<DrawData>();
539 2 : fromJson(root, dd);
540 2 : return RetVal<DrawDataPtr>::make_ok(dd);
541 2 : }
542 :
543 0 : ByteArray DrawDataJson::diffToJson(const Diff& diff)
544 : {
545 0 : IF_ASSERT_FAILED(diff.dataAdded && diff.dataRemoved) {
546 0 : return ByteArray();
547 : }
548 :
549 0 : JsonObject added;
550 0 : toJson(added, diff.dataAdded);
551 :
552 0 : JsonObject removed;
553 0 : toJson(removed, diff.dataRemoved);
554 :
555 0 : JsonObject root;
556 0 : root["type"] = "diff";
557 0 : root["added"] = added;
558 0 : root["removed"] = removed;
559 0 : return JsonDocument(root).toJson(JsonDocument::Format::Indented);
560 0 : }
561 :
562 0 : RetVal<Diff> DrawDataJson::diffFromJson(const ByteArray& json)
563 : {
564 0 : std::string err;
565 0 : JsonDocument doc = JsonDocument::fromJson(json, &err);
566 0 : if (!err.empty()) {
567 0 : RetVal<Diff> rv;
568 0 : rv.ret = make_ret(Ret::Code::UnknownError, err);
569 0 : return rv;
570 0 : }
571 :
572 0 : const JsonObject root = doc.rootObject();
573 0 : IF_ASSERT_FAILED(root.value("type").toStdString() == "diff") {
574 0 : RetVal<Diff> rv;
575 0 : rv.ret = make_ret(Ret::Code::UnknownError, err);
576 0 : return rv;
577 0 : }
578 :
579 0 : Diff diff;
580 0 : diff.dataAdded = std::make_shared<DrawData>();
581 0 : diff.dataRemoved = std::make_shared<DrawData>();
582 :
583 0 : JsonObject added = root.value("added").toObject();
584 0 : fromJson(added, diff.dataAdded);
585 :
586 0 : JsonObject removed = root.value("removed").toObject();
587 0 : fromJson(removed, diff.dataRemoved);
588 :
589 0 : return RetVal<Diff>::make_ok(diff);
590 0 : }
|