LCOV - code coverage report
Current view: top level - src/engraving/rendering/score - paint.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 69.4 % 111 77
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 "paint.h"
      23              : 
      24              : #include "draw/painter.h"
      25              : #include "dom/score.h"
      26              : #include "dom/page.h"
      27              : #include "dom/engravingitem.h"
      28              : 
      29              : #include "tdraw.h"
      30              : #include "debugpaint.h"
      31              : 
      32              : #include "log.h"
      33              : 
      34              : using namespace muse::draw;
      35              : using namespace mu::engraving;
      36              : using namespace mu::engraving::rendering::score;
      37              : 
      38            5 : void Paint::paintScore(Painter* painter, Score* score, const IScoreRenderer::ScorePaintOptions& opt)
      39              : {
      40            6 :     TRACEFUNC;
      41            5 :     if (!score) {
      42            0 :         return;
      43              :     }
      44              : 
      45            5 :     const std::vector<Page*>& pages = score->pages();
      46            5 :     if (pages.empty()) {
      47            0 :         return;
      48              :     }
      49              : 
      50              :     //! NOTE This is DPI of paint device,  ex screen, image, printer and etc.
      51              :     //! Should be set, but if not set, we will use our default DPI.
      52            5 :     const int DEVICE_DPI = opt.deviceDpi > 0 ? opt.deviceDpi : mu::engraving::DPI;
      53              : 
      54              :     //! NOTE Depending on the view mode,
      55              :     //! if the view mode is PAGE, then this is one page size (ex A4),
      56              :     //! if someone a continuous mode, then this is the size of the entire score.
      57            5 :     SizeF pageSize = pageSizeInch(score);
      58              : 
      59              :     // Setup Painter
      60            5 :     painter->setAntialiasing(true);
      61              : 
      62              :     //! NOTE To draw on the screen, no need to adjust the viewport,
      63              :     //! to draw on others (pdf, png, printer), we need to set the viewport
      64            5 :     if (opt.isSetViewport) {
      65            5 :         painter->setViewport(RectF(0.0, 0.0, std::lrint(pageSize.width() * DEVICE_DPI), std::lrint(pageSize.height() * DEVICE_DPI)));
      66            5 :         painter->setWindow(RectF(0.0, 0.0, std::lrint(pageSize.width() * engraving::DPI), std::lrint(pageSize.height() * engraving::DPI)));
      67              :     }
      68              : 
      69              :     // Setup score draw system
      70            5 :     mu::engraving::MScore::pdfPrinting = opt.isPrinting;
      71              : 
      72            5 :     const bool wasPrinting = score->printing();
      73            5 :     score->setPrinting(opt.isPrinting);
      74              : 
      75              :     // Setup page counts
      76            5 :     int fromPage = opt.fromPage >= 0 ? opt.fromPage : 0;
      77            5 :     int toPage = (opt.toPage >= 0 && opt.toPage < int(pages.size())) ? opt.toPage : (int(pages.size()) - 1);
      78              : 
      79           10 :     for (int copy = 0; copy < opt.copyCount; ++copy) {
      80            5 :         bool firstPage = true;
      81           10 :         for (int pi = fromPage; pi <= toPage; ++pi) {
      82            5 :             Page* page = pages.at(pi);
      83              : 
      84            5 :             PointF pagePos = page->pos();
      85            5 :             RectF pageRect = page->ldata()->bbox();
      86              : 
      87              :             //! NOTE Trim page margins, if need
      88            5 :             if (opt.trimMarginPixelSize >= 0) {
      89            0 :                 double trimMargin = static_cast<double>(opt.trimMarginPixelSize);
      90            0 :                 pageRect = page->tbbox().adjusted(-trimMargin, -trimMargin, trimMargin, trimMargin);
      91              :             }
      92              : 
      93              :             //! NOTE Check draw rect, usually for optimisation drawing on screen (draw only what we see)
      94            5 :             RectF drawRect;
      95            5 :             RectF pageAbsRect = pageRect.translated(pagePos);
      96            5 :             if (opt.frameRect.isValid()) {
      97            0 :                 if (pageAbsRect.right() < opt.frameRect.left()) {
      98            0 :                     continue;
      99              :                 }
     100              : 
     101            0 :                 if (pageAbsRect.left() > opt.frameRect.right()) {
     102            0 :                     break;
     103              :                 }
     104              : 
     105            0 :                 drawRect = opt.frameRect;
     106              :             } else {
     107            5 :                 drawRect = pageAbsRect;
     108              :             }
     109              : 
     110              :             //! NOTE Notify about new page (usually for paged paint device, ex pdf, printer)
     111            5 :             if (!firstPage) {
     112            0 :                 if (opt.onNewPage) {
     113            0 :                     opt.onNewPage();
     114              :                 }
     115              :             }
     116            5 :             firstPage = false;
     117              : 
     118            5 :             painter->beginObject("page_" + std::to_string(pi));
     119              : 
     120            5 :             if (opt.isPrinting && painter->hasClipping()) {
     121              :                 // prevent elements from being drawn off the edge of the page (e.g. too many staves),
     122              :                 // for now only in "Publish" mode (opt.isPrinting == true)
     123            0 :                 painter->setClipRect(pageAbsRect.intersected(drawRect));
     124              :             }
     125              : 
     126            5 :             if (opt.isMultiPage) {
     127            4 :                 painter->translate(pagePos);
     128            1 :             } else if (opt.trimMarginPixelSize >= 0) {
     129            0 :                 painter->translate(-pageRect.topLeft());
     130              :             }
     131              : 
     132              :             // Draw page sheet
     133            5 :             if (opt.onPaintPageSheet) {
     134            0 :                 opt.onPaintPageSheet(painter, page, pageRect);
     135            5 :             } else if (opt.printPageBackground) {
     136            4 :                 painter->fillRect(pageRect, Color::WHITE);
     137              :             }
     138              : 
     139              :             // Draw page elements
     140            5 :             bool disableClipping = false;
     141              : 
     142            5 :             if (!painter->hasClipping()) {
     143            5 :                 painter->setClipping(true);
     144            5 :                 painter->setClipRect(pageRect);
     145            5 :                 disableClipping = true;
     146              :             }
     147              : 
     148            5 :             std::vector<EngravingItem*> elements = page->items(drawRect.translated(-pagePos));
     149            5 :             paintItems(*painter, elements, opt);
     150              : 
     151            5 :             if (disableClipping) {
     152            5 :                 painter->setClipping(false);
     153              :             }
     154              : 
     155            5 :             if (!opt.isPrinting) {
     156            0 :                 DebugPaint::paintPageDebug(*painter, page, elements);
     157              :             }
     158              : 
     159            5 :             painter->endObject(); // page
     160              : 
     161            5 :             if (opt.isMultiPage) {
     162            4 :                 painter->translate(-pagePos);
     163            1 :             } else if (opt.trimMarginPixelSize >= 0) {
     164            0 :                 painter->translate(pageRect.topLeft());
     165              :             }
     166              : 
     167            5 :             if ((copy + 1) < opt.copyCount) {
     168              :                 //! NOTE Notify about new page (usually for paged paint device, ex pdf, printer)
     169              :                 //! for next copy
     170            0 :                 if (opt.onNewPage) {
     171            0 :                     opt.onNewPage();
     172              :                 }
     173              :             }
     174            5 :         }
     175              :     }
     176              : 
     177            5 :     score->setPrinting(wasPrinting);
     178            5 : }
     179              : 
     180            6 : SizeF Paint::pageSizeInch(const Score* score)
     181              : {
     182            6 :     if (!score) {
     183            0 :         return SizeF();
     184              :     }
     185              : 
     186              :     //! NOTE If now it is not PAGE view mode,
     187              :     //! then the page sizes will differ from the standard sizes (in PAGE view mode)
     188            6 :     if (score->npages() > 0) {
     189            6 :         const Page* page = score->pages().front();
     190            6 :         return SizeF(page->ldata()->bbox().width() / mu::engraving::DPI, page->ldata()->bbox().height() / mu::engraving::DPI);
     191              :     }
     192              : 
     193            0 :     return SizeF(score->style().styleD(Sid::pageWidth), score->style().styleD(Sid::pageHeight));
     194              : }
     195              : 
     196            0 : SizeF Paint::pageSizeInch(const Score* score, const IScoreRenderer::ScorePaintOptions& opt)
     197              : {
     198            0 :     if (!score) {
     199            0 :         return SizeF();
     200              :     }
     201              : 
     202            0 :     int pageNo = opt.fromPage >= 0 ? opt.fromPage : 0;
     203            0 :     if (pageNo >= int(score->npages())) {
     204            0 :         return SizeF();
     205              :     }
     206              : 
     207            0 :     const Page* page = score->pages().at(pageNo);
     208              : 
     209            0 :     RectF pageRect = page->ldata()->bbox();
     210              : 
     211              :     //! NOTE Trim page margins, if need
     212            0 :     if (opt.trimMarginPixelSize >= 0) {
     213            0 :         double trimMargin = static_cast<double>(opt.trimMarginPixelSize);
     214            0 :         pageRect = page->tbbox().adjusted(-trimMargin, -trimMargin, trimMargin, trimMargin);
     215              :     }
     216              : 
     217            0 :     return pageRect.size() / mu::engraving::DPI;
     218              : }
     219              : 
     220          380 : void Paint::paintItem(Painter& painter, const EngravingItem* item, const PaintOptions& opt)
     221              : {
     222          381 :     TRACEFUNC;
     223          380 :     if (item->ldata()->isSkipDraw()) {
     224            0 :         return;
     225              :     }
     226          380 :     item->itemDiscovered = false;
     227          380 :     PointF itemPosition(item->pagePos());
     228              : 
     229          380 :     painter.translate(itemPosition);
     230          380 :     TDraw::drawItem(item, &painter, opt);
     231          380 :     painter.translate(-itemPosition);
     232          380 : }
     233              : 
     234            5 : void Paint::paintItems(Painter& painter, const std::vector<EngravingItem*>& items, const PaintOptions& opt)
     235              : {
     236            6 :     TRACEFUNC;
     237            5 :     std::vector<EngravingItem*> sortedItems(items.begin(), items.end());
     238              : 
     239            5 :     std::sort(sortedItems.begin(), sortedItems.end(), mu::engraving::elementLessThan);
     240              : 
     241          385 :     for (const EngravingItem* item : sortedItems) {
     242          380 :         if (!item->isInteractionAvailable()) {
     243            0 :             continue;
     244              :         }
     245              : 
     246          380 :         paintItem(painter, item, opt);
     247              :     }
     248            5 : }
        

Generated by: LCOV version 2.0-1