LCOV - code coverage report
Current view: top level - src/engraving/rw - mscsaver.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 23.0 % 126 29
Test Date: 2026-11-05 04:07:02

            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 : }
        

Generated by: LCOV version 2.0-1