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