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 "debugpaint.h"
23 :
24 : #include "iengravingconfiguration.h"
25 : #include "iengravingcontextconfiguration.h" // IWYU pragma: keep
26 :
27 : //#include "accessibility/accessibleitem.h"
28 : #include "dom/page.h"
29 : #include "dom/score.h"
30 : #include "dom/system.h"
31 : #include "dom/measurebase.h"
32 : #include "dom/measure.h"
33 : #include "dom/note.h"
34 : #include "dom/segment.h"
35 :
36 : #include "tdraw.h"
37 :
38 : #include "log.h"
39 :
40 : using namespace muse::draw;
41 : using namespace mu::engraving;
42 : using namespace mu::engraving::rendering::score;
43 :
44 : static const Color DEBUG_ELTREE_SELECTED_COLOR(164, 0, 0);
45 :
46 : /// Generates a seemingly random but stable color based on a pointer address.
47 : /// If we would use really random colors, they would change on every redraw.
48 : /// Additional advantage: being able to see when an element is replaced with
49 : /// a different element during layout.
50 0 : static Color colorForPointer(const void* ptr)
51 : {
52 : static constexpr uint32_t rgbTotalMax = 600;
53 : static constexpr uint32_t mask = 0xFFFF;
54 : static constexpr double max = mask + 1.0;
55 :
56 0 : auto hash = std::hash<const void*> {}(ptr);
57 0 : uint32_t cmpnt1 = hash & mask;
58 0 : uint32_t cmpnt2 = (hash >> 16) & mask;
59 :
60 0 : uint32_t r = std::clamp(uint32_t(cmpnt1 / max * 255), 30u, 255u);
61 0 : uint32_t g = std::clamp(uint32_t(cmpnt2 / max * 255), 30u, 255u);
62 0 : uint32_t b = std::clamp(rgbTotalMax - r - g, 30u, 255u);
63 :
64 0 : return Color(r, g, b, 128);
65 : }
66 :
67 0 : void DebugPaint::paintElementDebug(Painter& painter, const EngravingItem* item)
68 : {
69 : // Elements tree
70 0 : bool isDiagnosticSelected = item->score()->elementsProvider() ? item->score()->elementsProvider()->isSelected(item) : false;
71 :
72 0 : PointF pos(item->pagePos());
73 0 : painter.translate(pos);
74 :
75 0 : RectF bbox = item->ldata()->bbox();
76 :
77 0 : if (item->isType(ElementType::SEGMENT)) {
78 0 : if (muse::RealIsNull(bbox.height())) {
79 0 : bbox.setHeight(10.0);
80 0 : LOGD() << "Segment bbox height is null";
81 : }
82 : }
83 :
84 0 : if (!bbox.isEmpty()) {
85 : // Draw shape
86 0 : if (item->configuration()->debuggingOptions().colorElementShapes
87 0 : && !item->isPage() && !item->isSystem() && !item->isStaffLines() && !item->isBox()) {
88 0 : PainterPath path;
89 0 : path.setFillRule(PainterPath::FillRule::WindingFill);
90 :
91 0 : Shape shape = item->shape();
92 0 : for (const RectF& rect : shape.elements()) {
93 0 : path.addRect(rect);
94 : }
95 :
96 0 : painter.setPen(PenStyle::NoPen);
97 0 : painter.setBrush(colorForPointer(item));
98 0 : painter.drawPath(path);
99 0 : }
100 :
101 0 : if (item->configuration()->debuggingOptions().showElementMasks) {
102 0 : PainterPath path;
103 0 : path.setFillRule(PainterPath::FillRule::WindingFill);
104 0 : for (const ShapeElement& el : item->ldata()->mask().elements()) {
105 0 : path.addRect(el);
106 : }
107 :
108 0 : painter.setPen(Color::BLACK);
109 0 : Brush brush(Color::BLACK);
110 0 : brush.setStyle(BrushStyle::BDiagPattern);
111 0 : painter.setBrush(brush);
112 0 : painter.drawPath(path);
113 0 : }
114 :
115 0 : if (item->isNote() && item->configuration()->debuggingOptions().showLineAttachPoints) {
116 0 : painter.setPen(Color::RED);
117 0 : Brush brush(Color::RED);
118 0 : painter.setBrush(brush);
119 0 : double radius = 0.1 * item->spatium();
120 0 : for (const LineAttachPoint& lap : toNote(item)->lineAttachPoints()) {
121 0 : PointF point = lap.pos();
122 0 : painter.drawEllipse(RectF(point.x() - radius, point.y() - radius, 2 * radius, 2 * radius));
123 : }
124 0 : }
125 :
126 : // Draw bbox
127 0 : if (isDiagnosticSelected || item->configuration()->debuggingOptions().showElementBoundingRects) {
128 0 : double scaling = painter.worldTransform().m11() / item->contextConfiguration()->guiScaling();
129 0 : Pen borderPen(DEBUG_ELTREE_SELECTED_COLOR, (item->selected() ? 2.0 : 1.0) / scaling);
130 :
131 0 : painter.setPen(borderPen);
132 0 : painter.setBrush(BrushStyle::NoBrush);
133 0 : painter.drawRect(bbox);
134 0 : }
135 : }
136 :
137 0 : painter.translate(-pos);
138 0 : }
139 :
140 0 : void DebugPaint::paintPageDebug(Painter& painter, const Page* page, const std::vector<EngravingItem*>& items)
141 : {
142 0 : if (items.empty()) {
143 0 : return;
144 : }
145 :
146 0 : auto options = items.front()->configuration()->debuggingOptions();
147 0 : if (!options.anyEnabled()) {
148 0 : return;
149 : }
150 :
151 0 : IF_ASSERT_FAILED(page) {
152 0 : return;
153 : }
154 :
155 0 : Score* score = page->score();
156 :
157 0 : IF_ASSERT_FAILED(score) {
158 0 : return;
159 : }
160 :
161 0 : double scaling = painter.worldTransform().m11() / items.front()->contextConfiguration()->guiScaling();
162 :
163 0 : painter.save();
164 :
165 0 : for (const EngravingItem* item : items) {
166 0 : paintElementDebug(painter, item);
167 : }
168 :
169 0 : if (options.showSystemBoundingRects) {
170 0 : painter.setBrush(BrushStyle::NoBrush);
171 0 : painter.setPen(Pen(Color::BLACK, 3.0 / scaling));
172 :
173 0 : for (const System* system : page->systems()) {
174 0 : PointF pt(system->ldata()->pos());
175 0 : double h = system->height() + system->minBottom() + system->minTop();
176 0 : painter.translate(pt);
177 0 : RectF rect(0.0, -system->minTop(), system->width(), h);
178 0 : painter.drawRect(rect);
179 0 : painter.translate(-pt);
180 : }
181 : }
182 :
183 0 : if (options.showSkylines) {
184 0 : for (const System* system : page->systems()) {
185 0 : for (const SysStaff* ss : system->staves()) {
186 0 : if (!ss->show()) {
187 0 : continue;
188 : }
189 :
190 0 : PointF pt(system->ldata()->pos().x(), system->ldata()->pos().y() + ss->y());
191 0 : painter.translate(pt);
192 0 : ss->skyline().paint(painter, 3);
193 0 : painter.translate(-pt);
194 : }
195 : }
196 : }
197 :
198 0 : if (options.colorSegmentShapes) {
199 0 : painter.setPen(PenStyle::NoPen);
200 :
201 0 : for (const System* system : page->systems()) {
202 0 : for (const MeasureBase* mb : system->measures()) {
203 0 : if (!mb->isMeasure()) {
204 0 : continue;
205 : }
206 :
207 0 : const Measure* m = toMeasure(mb);
208 :
209 0 : for (const Segment* s = m->first(); s; s = s->next()) {
210 0 : if (!s->enabled()) {
211 0 : continue;
212 : }
213 :
214 0 : painter.setBrush(colorForPointer(s));
215 :
216 0 : for (staff_idx_t i = 0; i < score->nstaves(); ++i) {
217 0 : PainterPath path;
218 0 : path.setFillRule(PainterPath::FillRule::WindingFill);
219 :
220 0 : Shape shape = s->shapes().at(i);
221 0 : for (const RectF& rect : shape.elements()) {
222 0 : path.addRect(rect);
223 : }
224 :
225 0 : PointF pt(s->pos().x() + m->pos().x() + system->pos().x(),
226 0 : system->staffYpage(i));
227 0 : painter.translate(pt);
228 0 : painter.drawPath(path);
229 0 : painter.translate(-pt);
230 0 : }
231 : }
232 : }
233 : }
234 : }
235 :
236 0 : if (options.showSegmentShapes) {
237 0 : painter.setBrush(BrushStyle::NoBrush);
238 0 : Pen pen(Color(238, 238, 144), 2.0 / scaling);
239 :
240 0 : for (const System* system : page->systems()) {
241 0 : for (const MeasureBase* mb : system->measures()) {
242 0 : if (!mb->isMeasure()) {
243 0 : continue;
244 : }
245 :
246 0 : const Measure* m = toMeasure(mb);
247 :
248 0 : for (const Segment* s = m->first(); s; s = s->next()) {
249 0 : pen.setStyle(s->enabled() ? PenStyle::SolidLine : PenStyle::DotLine);
250 0 : painter.setPen(pen);
251 :
252 0 : for (staff_idx_t i = 0; i < score->nstaves(); ++i) {
253 0 : PointF pt(s->pos().x() + m->pos().x() + system->pos().x(),
254 0 : system->staffYpage(i));
255 0 : painter.translate(pt);
256 0 : s->shapes().at(i).paint(painter);
257 0 : painter.translate(-pt);
258 : }
259 : }
260 : }
261 : }
262 0 : }
263 :
264 0 : if (options.markEmptyStaffVisibilityOverrides) {
265 0 : double _spatium = score->style().spatium();
266 :
267 0 : for (const System* system : page->systems()) {
268 0 : for (const MeasureBase* mb : system->measures()) {
269 0 : if (!mb->isMeasure()) {
270 0 : continue;
271 : }
272 :
273 0 : const Measure* m = toMeasure(mb);
274 :
275 0 : for (staff_idx_t staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) {
276 0 : AutoOnOff hideIfEmpty = m->hideStaffIfEmpty(staffIdx);
277 :
278 0 : Color fillColor;
279 0 : switch (hideIfEmpty) {
280 0 : case AutoOnOff::ON:
281 0 : fillColor = Color(255, 0, 0, 128);
282 0 : break;
283 0 : case AutoOnOff::OFF:
284 0 : fillColor = Color(0, 0, 255, 128);
285 0 : break;
286 0 : default:
287 0 : continue;
288 : }
289 :
290 0 : painter.setBrush(fillColor);
291 0 : painter.setPen(PenStyle::NoPen);
292 :
293 0 : painter.drawRect(m->staffPageBoundingRect(staffIdx).adjusted(0, -_spatium, 0, _spatium));
294 : }
295 : }
296 : }
297 : }
298 :
299 0 : if (options.markCorruptedMeasures && score->hasCorruptedMeasures()) {
300 0 : painter.setPen(Pen(Color::RED, 4.0));
301 0 : painter.setBrush(BrushStyle::NoBrush);
302 :
303 0 : double _spatium = score->style().spatium();
304 :
305 0 : for (const System* system : page->systems()) {
306 0 : for (const MeasureBase* mb : system->measures()) {
307 0 : if (!mb->isMeasure()) {
308 0 : continue;
309 : }
310 :
311 0 : const Measure* m = toMeasure(mb);
312 0 : for (size_t staffIdx = 0; staffIdx < score->nstaves(); staffIdx++) {
313 0 : if (m->corrupted(staffIdx)) {
314 0 : painter.drawRect(m->staffPageBoundingRect(staffIdx).adjusted(0, -_spatium, 0, _spatium));
315 : }
316 : }
317 : }
318 : }
319 : }
320 :
321 0 : painter.restore();
322 : }
|