Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-Studio-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 "mscsaver.h"
23 :
24 : #include <cmath>
25 :
26 : #include "global/io/buffer.h"
27 : #include "muse_framework_config.h"
28 :
29 : #ifdef MUSE_THREADS_SUPPORT
30 : #include <future>
31 : #endif
32 :
33 : #include "draw/painter.h"
34 :
35 : #include "dom/masterscore.h"
36 : #include "dom/excerpt.h"
37 : #include "dom/imageStore.h"
38 : #include "dom/mscore.h"
39 : #include "dom/page.h"
40 :
41 : #include "engraving/automation/internal/automationrw.h"
42 :
43 : #include "rwregister.h"
44 : #include "inoutdata.h"
45 :
46 : #include "log.h"
47 :
48 : using namespace mu;
49 : using namespace muse;
50 : using namespace muse::io;
51 : using namespace mu::engraving;
52 : using namespace mu::engraving::rw;
53 :
54 3 : bool MscSaver::writeMscz(MasterScore* score, MscWriter& mscWriter, bool createThumbnail,
55 : const write::WriteContext* ctx)
56 : {
57 4 : TRACEFUNC;
58 :
59 3 : IF_ASSERT_FAILED(mscWriter.isOpened()) {
60 0 : return false;
61 : }
62 :
63 : // Write style of MasterScore
64 : {
65 : //! NOTE The style is writing to a separate file only for the master score.
66 : //! At the moment, the style for the parts is still writing to the score file.
67 3 : ByteArray styleData;
68 3 : auto styleBuf = Buffer::opened(IODevice::WriteOnly, &styleData);
69 3 : score->style().write(&styleBuf);
70 3 : mscWriter.writeStyleFile(styleData);
71 3 : }
72 :
73 3 : WriteInOutData masterWriteOutData(score);
74 :
75 3 : if (ctx) {
76 0 : masterWriteOutData.ctx = *ctx;
77 : }
78 :
79 : // Write MasterScore
80 : {
81 3 : ByteArray scoreData;
82 3 : auto scoreBuf = Buffer::opened(IODevice::ReadWrite, &scoreData);
83 :
84 3 : RWRegister::writer()->writeScore(score, &scoreBuf, &masterWriteOutData);
85 :
86 3 : mscWriter.writeScoreFile(scoreData);
87 3 : }
88 :
89 : // Write Excerpts
90 : {
91 3 : if (!ctx || !ctx->shouldWriteRange()) {
92 3 : const std::vector<Excerpt*>& excerpts = score->excerpts();
93 :
94 : struct ExcerptData {
95 : String fileName;
96 : ByteArray styleData;
97 : ByteArray scoreData;
98 : };
99 :
100 0 : auto serializeExcerpt = [masterWriteOutData, score](Excerpt* excerpt, size_t excerptIndex) -> ExcerptData {
101 0 : Score* partScore = excerpt->excerptScore();
102 0 : IF_ASSERT_FAILED(partScore && partScore != score) {
103 0 : return ExcerptData();
104 : }
105 :
106 0 : excerpt->updateFileName(excerptIndex);
107 :
108 0 : ExcerptData data;
109 0 : data.fileName = excerpt->fileName();
110 :
111 0 : auto styleBuf = Buffer::opened(IODevice::WriteOnly, &data.styleData);
112 0 : partScore->style().write(&styleBuf);
113 :
114 0 : WriteInOutData writeOutData = masterWriteOutData;
115 0 : auto scoreBuf = Buffer::opened(IODevice::ReadWrite, &data.scoreData);
116 0 : RWRegister::writer()->writeScore(partScore, &scoreBuf, &writeOutData);
117 :
118 0 : return data;
119 0 : };
120 :
121 : #ifdef MUSE_THREADS_SUPPORT
122 : // Parallelize excerpt serialization (CPU-bound, independent per excerpt)
123 3 : std::vector<std::future<ExcerptData> > futures;
124 3 : futures.reserve(excerpts.size());
125 :
126 3 : for (size_t excerptIndex = 0; excerptIndex < excerpts.size(); ++excerptIndex) {
127 0 : Excerpt* excerpt = excerpts.at(excerptIndex);
128 :
129 0 : futures.push_back(std::async(std::launch::async, [serializeExcerpt, excerpt, excerptIndex]() {
130 0 : return serializeExcerpt(excerpt, excerptIndex);
131 : }));
132 : }
133 :
134 : // Wait for all serializations to complete, then write to mscWriter sequentially
135 : // (MscWriter is not thread-safe)
136 3 : for (auto& future : futures) {
137 0 : ExcerptData data = future.get();
138 0 : mscWriter.addExcerptStyleFile(data.fileName, data.styleData);
139 0 : mscWriter.addExcerptFile(data.fileName, data.scoreData);
140 0 : }
141 : #else
142 : for (size_t excerptIndex = 0; excerptIndex < excerpts.size(); ++excerptIndex) {
143 : Excerpt* excerpt = excerpts.at(excerptIndex);
144 :
145 : ExcerptData data = serializeExcerpt(excerpt, excerptIndex);
146 : mscWriter.addExcerptStyleFile(data.fileName, data.styleData);
147 : mscWriter.addExcerptFile(data.fileName, data.scoreData);
148 : }
149 : #endif
150 3 : }
151 : }
152 :
153 : // Write ChordList
154 : {
155 3 : ChordList* chordList = score->chordList();
156 3 : if (chordList->customChordList() && !chordList->empty()) {
157 0 : ByteArray chlData;
158 0 : auto chlBuf = Buffer::opened(IODevice::WriteOnly, &chlData);
159 0 : chordList->write(&chlBuf);
160 0 : mscWriter.writeChordListFile(chlData);
161 0 : }
162 : }
163 :
164 : // Write images
165 : {
166 3 : for (ImageStoreItem* ip : imageStore) {
167 0 : if (!ip->isUsed(score)) {
168 0 : continue;
169 : }
170 0 : ByteArray data = ip->buffer();
171 0 : mscWriter.addImageFile(String::fromStdString(ip->hashName()), data);
172 0 : }
173 : }
174 :
175 : // Write thumbnail
176 : {
177 3 : if (createThumbnail && !score->pages().empty()) {
178 0 : auto pixmap = this->createThumbnail(score);
179 :
180 0 : ByteArray ba;
181 0 : auto b = Buffer::opened(IODevice::WriteOnly, &ba);
182 0 : imageProvider()->saveAsPng(pixmap, &b);
183 0 : mscWriter.writeThumbnailFile(ba);
184 0 : }
185 : }
186 :
187 : // Write automation
188 : {
189 3 : if (score->automationData()) {
190 0 : ByteArray data = AutomationRW::write(*score->automationData(), false /*writeGenerated*/);
191 0 : if (!data.empty()) {
192 0 : mscWriter.writeAutomationJsonFile(data);
193 : }
194 0 : }
195 : }
196 :
197 3 : return true;
198 3 : }
199 :
200 0 : bool MscSaver::exportPart(Score* partScore, MscWriter& mscWriter)
201 : {
202 : // Write excerpt style as main
203 : {
204 0 : ByteArray excerptStyleData;
205 0 : auto styleStyleBuf = Buffer::opened(IODevice::WriteOnly, &excerptStyleData);
206 0 : partScore->style().write(&styleStyleBuf);
207 :
208 0 : mscWriter.writeStyleFile(excerptStyleData);
209 0 : }
210 :
211 : // Add the master score's metaTags to the part
212 0 : const std::map<String, String> partTags = partScore->metaTags();
213 0 : std::map<String, String> effectiveTags = partScore->masterScore()->metaTags();
214 0 : for (const auto& [key, val] : partTags) {
215 0 : effectiveTags.insert_or_assign(key, val);
216 : }
217 0 : partScore->setMetaTags(effectiveTags);
218 :
219 : // Write excerpt as main score
220 : {
221 0 : ByteArray excerptData;
222 0 : auto excerptBuf = Buffer::opened(IODevice::WriteOnly, &excerptData);
223 :
224 0 : rw::RWRegister::writer()->writeScore(partScore, &excerptBuf);
225 :
226 0 : mscWriter.writeScoreFile(excerptData);
227 0 : }
228 :
229 : // restore in-memory part score's metaTags
230 0 : partScore->setMetaTags(partTags);
231 :
232 : // Write thumbnail
233 : {
234 0 : if (!partScore->pages().empty()) {
235 0 : auto pixmap = createThumbnail(partScore);
236 :
237 0 : ByteArray ba;
238 0 : auto b = Buffer::opened(IODevice::WriteOnly, &ba);
239 0 : imageProvider()->saveAsPng(pixmap, &b);
240 0 : mscWriter.writeThumbnailFile(ba);
241 0 : }
242 : }
243 :
244 0 : return true;
245 0 : }
246 :
247 0 : std::shared_ptr<muse::draw::Pixmap> MscSaver::createThumbnail(Score* score)
248 : {
249 0 : TRACEFUNC;
250 :
251 0 : LayoutMode mode = score->layoutMode();
252 0 : score->switchToPageMode();
253 :
254 0 : Page* page = score->pages().at(0);
255 0 : RectF fr = page->pageBoundingRect();
256 0 : double mag = 512.0 / std::max(fr.width(), fr.height());
257 0 : int w = int(fr.width() * mag);
258 0 : int h = int(fr.height() * mag);
259 :
260 0 : int dpm = lrint(DPMM * 1000.0);
261 :
262 0 : auto pixmap = imageProvider()->createPixmap(w, h, dpm, configuration()->thumbnailBackgroundColor());
263 :
264 0 : auto painterProvider = imageProvider()->painterForImage(pixmap);
265 0 : muse::draw::Painter p(painterProvider, "thumbnail");
266 :
267 0 : p.setAntialiasing(true);
268 0 : p.scale(mag, mag);
269 :
270 0 : rendering::IScoreRenderer::ScorePaintOptions opt;
271 0 : opt.isPrinting = true;
272 0 : opt.isSetViewport = false;
273 0 : opt.printPageBackground = false;
274 0 : opt.fromPage = 0;
275 0 : opt.toPage = 0;
276 0 : scoreRenderer()->paintScore(&p, score, opt);
277 :
278 0 : p.endDraw();
279 :
280 0 : if (score->layoutMode() != mode) {
281 0 : score->setLayoutMode(mode);
282 0 : score->doLayout();
283 : }
284 0 : return pixmap;
285 0 : }
|