Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-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 "painter.h"
23 :
24 : #include "types/brush.h"
25 : #include "types/painterpath.h"
26 :
27 : #ifndef NO_QT_SUPPORT
28 : #include "internal/qpainterprovider.h"
29 : #endif
30 :
31 : #include "log.h"
32 :
33 : using namespace muse;
34 : using namespace muse::draw;
35 :
36 : IPaintProviderPtr Painter::extended;
37 : bool PainterItemMarker::enabled = true;
38 :
39 7 : Painter::Painter(IPaintProviderPtr provider, const std::string& name)
40 7 : : m_provider(provider), m_name(name)
41 : {
42 7 : init();
43 7 : }
44 :
45 : #ifndef NO_QT_SUPPORT
46 14 : Painter::Painter(QPaintDevice* dp, const std::string& name)
47 14 : : m_name(name)
48 : {
49 14 : m_provider = QPainterProvider::make(dp);
50 14 : init();
51 14 : }
52 :
53 4 : Painter::Painter(QPainter* qp, const std::string& name, bool ownsQPainter)
54 4 : : m_name(name)
55 : {
56 4 : m_provider = QPainterProvider::make(qp, ownsQPainter);
57 4 : init();
58 4 : }
59 :
60 : #endif
61 :
62 25 : Painter::~Painter()
63 : {
64 25 : endTarget(false);
65 25 : }
66 :
67 25 : void Painter::init()
68 : {
69 25 : m_provider->beginTarget(m_name);
70 25 : if (extended) {
71 0 : extended->beginTarget(m_name);
72 : }
73 :
74 25 : State st;
75 25 : st.worldTransform = m_provider->transform();
76 25 : st.isWxF = true;
77 25 : st.transform = st.worldTransform;
78 :
79 25 : m_states = std::stack<State>();
80 25 : m_states.push(std::move(st));
81 25 : }
82 :
83 23 : IPaintProviderPtr Painter::provider() const
84 : {
85 23 : return m_provider;
86 : }
87 :
88 0 : void Painter::setProvider(const IPaintProviderPtr& p, bool reinit)
89 : {
90 0 : m_provider = p;
91 0 : if (reinit) {
92 0 : init();
93 : }
94 0 : }
95 :
96 16 : bool Painter::endDraw()
97 : {
98 16 : return endTarget(true);
99 : }
100 :
101 41 : bool Painter::endTarget(bool endDraw)
102 : {
103 41 : m_provider->beforeEndTargetHook(this);
104 41 : if (extended) {
105 0 : extended->beforeEndTargetHook(this);
106 : }
107 :
108 41 : bool ok = m_provider->endTarget(endDraw);
109 41 : if (extended) {
110 0 : extended->endTarget(endDraw);
111 : }
112 41 : return ok;
113 : }
114 :
115 0 : bool Painter::isActive() const
116 : {
117 0 : return m_provider->isActive();
118 : }
119 :
120 9 : void Painter::beginObject(const std::string& name)
121 : {
122 9 : m_provider->beginObject(name);
123 9 : if (extended) {
124 0 : extended->beginObject(name);
125 : }
126 9 : }
127 :
128 9 : void Painter::endObject()
129 : {
130 9 : m_provider->endObject();
131 9 : if (extended) {
132 0 : extended->endObject();
133 : }
134 9 : }
135 :
136 6 : void Painter::setAntialiasing(bool arg)
137 : {
138 6 : m_provider->setAntialiasing(arg);
139 6 : if (extended) {
140 0 : extended->setAntialiasing(arg);
141 : }
142 6 : }
143 :
144 0 : void Painter::setCompositionMode(CompositionMode mode)
145 : {
146 0 : m_provider->setCompositionMode(mode);
147 0 : if (extended) {
148 0 : extended->setCompositionMode(mode);
149 : }
150 0 : }
151 :
152 502 : void Painter::setFont(const Font& font)
153 : {
154 502 : m_provider->setFont(font);
155 502 : if (extended) {
156 0 : extended->setFont(font);
157 : }
158 502 : }
159 :
160 251 : const Font& Painter::font() const
161 : {
162 251 : return m_provider->font();
163 : }
164 :
165 396 : void Painter::setPen(const Pen& pen)
166 : {
167 396 : m_provider->setPen(pen);
168 396 : if (extended) {
169 0 : extended->setPen(pen);
170 : }
171 396 : }
172 :
173 0 : void Painter::setNoPen()
174 : {
175 0 : setPen(Pen(PenStyle::NoPen));
176 0 : }
177 :
178 4 : const Pen& Painter::pen() const
179 : {
180 4 : return m_provider->pen();
181 : }
182 :
183 10 : void Painter::setBrush(const Brush& brush)
184 : {
185 10 : m_provider->setBrush(brush);
186 10 : if (extended) {
187 0 : extended->setBrush(brush);
188 : }
189 10 : }
190 :
191 4 : const Brush& Painter::brush() const
192 : {
193 4 : return m_provider->brush();
194 : }
195 :
196 302 : void Painter::save()
197 : {
198 302 : State newSt = m_states.top();
199 302 : m_states.push(newSt);
200 :
201 302 : m_provider->save();
202 302 : if (extended) {
203 0 : extended->save();
204 : }
205 302 : }
206 :
207 302 : void Painter::restore()
208 : {
209 302 : if (m_states.size() > 0) {
210 302 : m_states.pop();
211 : }
212 :
213 302 : m_provider->restore();
214 302 : if (extended) {
215 0 : extended->restore();
216 : }
217 302 : }
218 :
219 251 : double Painter::deviceLogicalDpi() const
220 : {
221 251 : return m_provider->deviceLogicalDpi();
222 : }
223 :
224 5 : void Painter::setWorldTransform(const Transform& matrix, bool combine)
225 : {
226 5 : State& st = editableState();
227 5 : if (combine) {
228 0 : st.worldTransform = matrix * st.worldTransform; // combines
229 : } else {
230 5 : st.worldTransform = matrix; // set new matrix
231 : }
232 5 : st.isWxF = true;
233 5 : updateMatrix();
234 5 : }
235 :
236 5 : const Transform& Painter::worldTransform() const
237 : {
238 5 : return state().worldTransform;
239 : }
240 :
241 251 : void Painter::scale(double sx, double sy)
242 : {
243 251 : State& st = editableState();
244 251 : st.worldTransform.scale(sx, sy);
245 251 : st.isWxF = true;
246 251 : updateMatrix();
247 251 : }
248 :
249 0 : void Painter::rotate(double angle)
250 : {
251 0 : State& st = editableState();
252 0 : st.worldTransform.rotate(angle);
253 0 : st.isWxF = true;
254 0 : updateMatrix();
255 0 : }
256 :
257 774 : void Painter::translate(double dx, double dy)
258 : {
259 774 : State& st = editableState();
260 774 : st.worldTransform.translate(dx, dy);
261 774 : st.isWxF = true;
262 774 : updateMatrix();
263 774 : }
264 :
265 1 : RectF Painter::window() const
266 : {
267 1 : return state().window;
268 : }
269 :
270 10 : void Painter::setWindow(const RectF& window)
271 : {
272 10 : State& st = editableState();
273 10 : st.window = window;
274 10 : st.isVxF = true;
275 10 : updateViewTransform();
276 :
277 : // for debug purpose
278 10 : m_provider->setWindow(window);
279 10 : if (extended) {
280 0 : extended->setWindow(window);
281 : }
282 10 : }
283 :
284 1 : RectF Painter::viewport() const
285 : {
286 1 : return state().viewport;
287 : }
288 :
289 11 : void Painter::setViewport(const RectF& viewport)
290 : {
291 11 : State& st = editableState();
292 11 : st.viewport = viewport;
293 11 : st.isVxF = true;
294 11 : updateViewTransform();
295 :
296 : // for debug purpose
297 11 : m_provider->setViewport(viewport);
298 11 : if (extended) {
299 0 : extended->setViewport(viewport);
300 : }
301 11 : }
302 :
303 : // drawing functions
304 :
305 0 : void Painter::fillPath(const PainterPath& path, const Brush& brush)
306 : {
307 0 : Pen oldPen = this->pen();
308 0 : Brush oldBrush = this->brush();
309 0 : setPen(Pen(PenStyle::NoPen));
310 0 : setBrush(brush);
311 :
312 0 : drawPath(path);
313 :
314 0 : setPen(oldPen);
315 0 : setBrush(oldBrush);
316 0 : }
317 :
318 0 : void Painter::strokePath(const PainterPath& path, const Pen& pen)
319 : {
320 0 : Pen oldPen = this->pen();
321 0 : Brush oldBrush = this->brush();
322 0 : setPen(pen);
323 0 : setBrush(BrushStyle::NoBrush);
324 :
325 0 : drawPath(path);
326 :
327 0 : setPen(oldPen);
328 0 : setBrush(oldBrush);
329 0 : }
330 :
331 6 : void Painter::drawPath(const PainterPath& path)
332 : {
333 6 : m_provider->drawPath(path);
334 6 : if (extended) {
335 0 : extended->drawPath(path);
336 : }
337 6 : }
338 :
339 151 : void Painter::drawLines(const LineF* lines, size_t lineCount)
340 : {
341 382 : for (size_t i = 0; i < lineCount; ++i) {
342 231 : PointF pts[2] = { lines[i].p1(), lines[i].p2() };
343 231 : if (pts[0] == pts[1]) {
344 0 : LOGE() << "draw point not implemented";
345 : }
346 231 : drawPolyline(pts, 2);
347 : }
348 151 : }
349 :
350 0 : void Painter::drawLines(const PointF* pointPairs, size_t lineCount)
351 : {
352 : static_assert(sizeof(LineF) == 2 * sizeof(PointF), "must be: sizeof(LineF) == 2 * sizeof(PointF)");
353 :
354 0 : drawLines((const LineF*)pointPairs, lineCount);
355 0 : }
356 :
357 6 : void Painter::drawRects(const RectF* rects, size_t rectCount)
358 : {
359 12 : for (size_t i = 0; i < rectCount; ++i) {
360 6 : PainterPath path;
361 6 : path.addRect(rects[i]);
362 6 : if (path.isEmpty()) {
363 0 : continue;
364 : }
365 6 : drawPath(path);
366 6 : }
367 6 : }
368 :
369 0 : void Painter::drawEllipse(const RectF& rect)
370 : {
371 0 : PainterPath path;
372 0 : path.addEllipse(rect);
373 0 : m_provider->drawPath(path);
374 0 : if (extended) {
375 0 : extended->drawPath(path);
376 : }
377 0 : }
378 :
379 231 : void Painter::drawPolyline(const PointF* points, size_t pointCount)
380 : {
381 231 : m_provider->drawPolygon(points, pointCount, PolygonMode::Polyline);
382 231 : if (extended) {
383 0 : extended->drawPolygon(points, pointCount, PolygonMode::Polyline);
384 : }
385 231 : }
386 :
387 0 : void Painter::drawPolygon(const PointF* points, size_t pointCount, FillRule fillRule)
388 : {
389 0 : PolygonMode mode = (fillRule == FillRule::OddEvenFill) ? PolygonMode::OddEven : PolygonMode::Winding;
390 0 : m_provider->drawPolygon(points, pointCount, mode);
391 0 : if (extended) {
392 0 : extended->drawPolygon(points, pointCount, mode);
393 : }
394 0 : }
395 :
396 0 : void Painter::drawConvexPolygon(const PointF* points, size_t pointCount)
397 : {
398 0 : m_provider->drawPolygon(points, pointCount, PolygonMode::Convex);
399 0 : if (extended) {
400 0 : extended->drawPolygon(points, pointCount, PolygonMode::Convex);
401 : }
402 0 : }
403 :
404 0 : void Painter::drawArc(const RectF& r, int a, int alen)
405 : {
406 : //! NOTE Copied from QPainter source code
407 :
408 0 : RectF rect = r.normalized();
409 :
410 0 : PainterPath path;
411 0 : path.arcMoveTo(rect, a / 16.0);
412 0 : path.arcTo(rect, a / 16.0, alen / 16.0);
413 0 : strokePath(path, pen());
414 0 : }
415 :
416 0 : void Painter::drawRoundedRect(const RectF& rect, double xRadius, double yRadius)
417 : {
418 0 : if (xRadius <= 0 || yRadius <= 0) { // draw normal rectangle
419 0 : drawRect(rect);
420 0 : return;
421 : }
422 :
423 0 : PainterPath path;
424 0 : path.addRoundedRect(rect, xRadius, yRadius);
425 0 : drawPath(path);
426 0 : }
427 :
428 251 : void Painter::applyFontSizeScaling()
429 : {
430 : static constexpr double MU_ENGRAVING_DPI = 1200; // Same as mu::engraving::DPI. TODO: pass as parameter.
431 251 : Font f = font();
432 251 : double scaledPointSize = f.pointSizeF() * MU_ENGRAVING_DPI / deviceLogicalDpi();
433 251 : f.setPointSizeF(scaledPointSize);
434 251 : setFont(f);
435 251 : }
436 :
437 0 : void Painter::drawText(const PointF& point, const String& text)
438 : {
439 0 : applyFontSizeScaling();
440 :
441 0 : m_provider->drawText(point, text);
442 0 : if (extended) {
443 0 : extended->drawText(point, text);
444 : }
445 0 : }
446 :
447 0 : void Painter::drawText(const RectF& rect, Alignment alignment, TextFlags textFlags, const String& text)
448 : {
449 0 : applyFontSizeScaling();
450 :
451 0 : m_provider->drawText(rect, alignment, textFlags, text);
452 0 : if (extended) {
453 0 : extended->drawText(rect, alignment, textFlags, text);
454 : }
455 0 : }
456 :
457 251 : void Painter::drawSymbol(const PointF& point, char32_t ucs4Code)
458 : {
459 251 : applyFontSizeScaling();
460 :
461 251 : m_provider->drawSymbol(point, ucs4Code);
462 251 : if (extended) {
463 0 : extended->drawSymbol(point, ucs4Code);
464 : }
465 251 : }
466 :
467 4 : void Painter::fillRect(const RectF& rect, const Brush& brush)
468 : {
469 4 : Pen oldPen = this->pen();
470 4 : Brush oldBrush = this->brush();
471 4 : setPen(Pen(PenStyle::NoPen));
472 4 : setBrush(brush);
473 :
474 4 : drawRect(rect);
475 :
476 4 : setBrush(oldBrush);
477 4 : setPen(oldPen);
478 4 : }
479 :
480 0 : void Painter::drawPixmap(const PointF& point, const Pixmap& pm)
481 : {
482 0 : m_provider->drawPixmap(point, pm);
483 0 : if (extended) {
484 0 : extended->drawPixmap(point, pm);
485 : }
486 0 : }
487 :
488 0 : void Painter::drawTiledPixmap(const RectF& rect, const Pixmap& pm, const PointF& offset)
489 : {
490 0 : m_provider->drawTiledPixmap(rect, pm, offset);
491 0 : if (extended) {
492 0 : extended->drawTiledPixmap(rect, pm, offset);
493 : }
494 0 : }
495 :
496 : #ifndef NO_QT_SUPPORT
497 0 : void Painter::drawPixmap(const PointF& point, const QPixmap& pm)
498 : {
499 0 : m_provider->drawPixmap(point, pm);
500 0 : if (extended) {
501 0 : extended->drawPixmap(point, pm);
502 : }
503 0 : }
504 :
505 0 : void Painter::drawTiledPixmap(const RectF& rect, const QPixmap& pm, const PointF& offset)
506 : {
507 0 : m_provider->drawTiledPixmap(rect, pm, offset);
508 0 : if (extended) {
509 0 : extended->drawTiledPixmap(rect, pm, offset);
510 : }
511 0 : }
512 :
513 : #endif
514 :
515 2123 : Painter::State& Painter::editableState()
516 : {
517 2123 : return m_states.top();
518 : }
519 :
520 7 : const Painter::State& Painter::state() const
521 : {
522 7 : return m_states.top();
523 : }
524 :
525 21 : void Painter::updateViewTransform()
526 : {
527 21 : State& st = editableState();
528 21 : if (st.window.isNull()) {
529 8 : st.viewTransform = Transform();
530 8 : updateMatrix();
531 8 : return;
532 : }
533 :
534 13 : double scaleW = double(st.viewport.width()) / double(st.window.width());
535 13 : double scaleH = double(st.viewport.height()) / double(st.window.height());
536 13 : st.viewTransform = Transform(scaleW, 0, 0, scaleH, st.viewport.x() - st.window.x() * scaleW, st.viewport.y() - st.window.y() * scaleH);
537 13 : updateMatrix();
538 : }
539 :
540 1051 : void Painter::updateMatrix()
541 : {
542 1051 : Painter::State& st = editableState();
543 1051 : st.transform = st.isWxF ? st.worldTransform : Transform();
544 1051 : if (st.isVxF) {
545 1049 : st.transform *= st.viewTransform;
546 : }
547 :
548 1051 : m_provider->setTransform(st.transform);
549 1051 : if (extended) {
550 0 : extended->setTransform(st.transform);
551 : }
552 1051 : }
553 :
554 10 : bool Painter::hasClipping() const
555 : {
556 10 : return m_provider->hasClipping();
557 : }
558 :
559 5 : void Painter::setClipRect(const RectF& rect)
560 : {
561 5 : m_provider->setClipRect(rect);
562 5 : }
563 :
564 0 : void Painter::setMask(const RectF& background, const std::vector<RectF>& maskRects)
565 : {
566 0 : m_provider->setMask(background, maskRects);
567 0 : }
568 :
569 10 : void Painter::setClipping(bool enable)
570 : {
571 10 : m_provider->setClipping(enable);
572 10 : }
|