KOSMIndoorMap

painterrenderer.cpp
1/*
2 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "painterrenderer.h"
8#include "stackblur_p.h"
9#include "render-logging.h"
10
11#include <KOSMIndoorMap/SceneGraph>
12#include <KOSMIndoorMap/View>
13
14#include <QDebug>
15#include <QElapsedTimer>
16#include <QFontMetricsF>
17#include <QGuiApplication>
18#include <QImage>
19#include <QLinearGradient>
20#include <QPainter>
21
22#include <cmath>
23
24using namespace KOSMIndoorMap;
25
26PainterRenderer::PainterRenderer() = default;
27PainterRenderer::~PainterRenderer() = default;
28
29void PainterRenderer::setPainter(QPainter *painter)
30{
31 m_painter = painter;
32}
33
34void PainterRenderer::render(const SceneGraph &sg, View *view)
35{
36 QElapsedTimer frameTimer;
37 frameTimer.start();
38
39 m_view = view;
40 beginRender();
41 renderBackground(sg.backgroundColor());
42
43 for (const auto &layerOffsets : sg.layerOffsets()) {
44 const auto layerBegin = sg.itemsBegin(layerOffsets);
45 const auto layerEnd = sg.itemsEnd(layerOffsets);
46 //qDebug() << "rendering layer" << (*layerBegin)->layer;
47
48 // select elements currently in view
49 m_renderBatch.clear();
50 m_renderBatch.reserve(layerOffsets.second - layerOffsets.first);
51 const QRectF screenRect(QPointF(0, 0), QSizeF(m_view->screenWidth(), m_view->screenHeight()));
52 for (auto it = layerBegin; it != layerEnd; ++it) {
53 if ((*it).payload->inSceneSpace() && m_view->viewport().intersects((*it).payload->boundingRect(view))) {
54 m_renderBatch.push_back((*it).payload.get());
55 }
56 if ((*it).payload->inHUDSpace()) {
57 auto bbox = (*it).payload->boundingRect(view);
58 bbox.moveCenter(m_view->mapSceneToScreen(bbox.center()));
59 if (screenRect.intersects(bbox)) {
60 m_renderBatch.push_back((*it).payload.get());
61 }
62 }
63 }
64
65 for (auto phase : {SceneGraphItemPayload::FillPhase, SceneGraphItemPayload::CasingPhase, SceneGraphItemPayload::StrokePhase, SceneGraphItemPayload::IconPhase, SceneGraphItemPayload::LabelPhase}) {
66 beginPhase(phase);
67 prepareBatch(phase);
68 for (auto it = m_renderBatch.begin(); it != m_renderBatch.end(); ++it) {
69 const auto &item = (*it);
70 if ((item->renderPhases() & phase) == 0) {
71 continue;
72 }
73
74 if (auto i = dynamic_cast<PolygonItem*>(item)) {
75 renderPolygon(i, phase);
76 } else if (auto i = dynamic_cast<MultiPolygonItem*>(item)) {
77 renderMultiPolygon(i, phase);
78 } else if (auto i = dynamic_cast<PolylineItem*>(item)) {
79 renderPolyline(i, phase);
80 } else if (auto i = dynamic_cast<LabelItem*>(item)) {
81 // skip if a higher up item would overlap this one, unless that is explicitly allowed
82 if (phase == SceneGraphItemPayload::IconPhase) {
83 if (!i->iconHidden) {
84 renderLabel(i, phase);
85 }
86 } else if (phase == SceneGraphItemPayload::LabelPhase) {
87 if (!i->textHidden) {
88 renderLabel(i, phase);
89 }
90 }
91 } else {
92 qCritical() << "Unsupported scene graph item!";
93 }
94 }
95 }
96 }
97
98 renderForeground(sg.backgroundColor());
99 endRender();
100 m_view = nullptr;
101
102 qCDebug(RenderLog) << "rendering took:" << frameTimer.elapsed() << "ms for" << sg.items().size() << "items on" << sg.layerOffsets().size() << "layers";
103}
104
105void PainterRenderer::beginRender()
106{
107 m_painter->save();
108}
109
110void PainterRenderer::renderBackground(const QColor &bgColor)
111{
112 m_painter->setTransform(m_view->deviceTransform());
113 m_painter->fillRect(0, 0, m_view->screenWidth(), m_view->screenHeight(), bgColor);
114}
115
116void PainterRenderer::beginPhase(SceneGraphItemPayload::RenderPhase phase)
117{
118 switch (phase) {
119 case SceneGraphItemPayload::NoPhase:
120 Q_UNREACHABLE();
121 case SceneGraphItemPayload::FillPhase:
122 m_painter->setPen(Qt::NoPen);
123 m_painter->setTransform(m_view->sceneToScreenTransform() * m_view->deviceTransform());
124 m_painter->setClipRect(m_view->viewport().intersected(m_view->sceneBoundingBox()));
125 m_painter->setRenderHint(QPainter::Antialiasing, false);
126 break;
127 case SceneGraphItemPayload::CasingPhase:
128 case SceneGraphItemPayload::StrokePhase:
129 m_painter->setBrush(Qt::NoBrush);
130 m_painter->setTransform(m_view->sceneToScreenTransform() * m_view->deviceTransform());
131 m_painter->setClipRect(m_view->viewport().intersected(m_view->sceneBoundingBox()));
132 m_painter->setRenderHint(QPainter::Antialiasing, true);
133 break;
134 case SceneGraphItemPayload::IconPhase:
135 case SceneGraphItemPayload::LabelPhase:
136 m_painter->setTransform(m_view->deviceTransform());
137 m_painter->setRenderHint(QPainter::Antialiasing, true);
139 break;
140 }
141}
142
143void PainterRenderer::prepareBatch(SceneGraphItemPayload::RenderPhase phase)
144{
145 switch (phase) {
146 case SceneGraphItemPayload::NoPhase:
147 Q_UNREACHABLE();
148 case SceneGraphItemPayload::FillPhase:
149 case SceneGraphItemPayload::CasingPhase:
150 case SceneGraphItemPayload::StrokePhase:
151 break;
152 case SceneGraphItemPayload::IconPhase:
153 // place icons/shields starting at the top (== back of m_renderBatch)
154 // and hide everything they would cover (unless overlap is explicitly allowed)
155 // TODO ensure minimum repeat distance between shields here
156 for (auto it = m_renderBatch.rbegin(); it != m_renderBatch.rend(); ++it) {
157 if (((*it)->renderPhases() & SceneGraphItemPayload::IconPhase) == 0) {
158 continue;
159 }
160 const auto item = dynamic_cast<LabelItem*>(*it);
161 if (!item) {
162 continue;
163 }
164 item->iconHidden = false;
165 if (item->allowIconOverlap) {
166 continue;
167 }
168
169 QRectF bbox;
170 if (item->hasShield()) {
171 bbox = item->shieldHitBox(m_view);
172 } else {
173 bbox = item->iconHitBox(m_view);
174 }
175
176 for (auto it2 = it.base(); it2 != m_renderBatch.end(); ++it2) {
177 if (((*it2)->renderPhases() & SceneGraphItemPayload::IconPhase) == 0) {
178 continue;
179 }
180 const auto otherItem = dynamic_cast<LabelItem*>((*it2));
181 if (!otherItem || otherItem->allowIconOverlap) { // TODO remove the allowIconOverlap check, this is a workaround for wrong z order
182 continue;
183 }
184
185 QRectF bbox2;
186 if (otherItem->hasShield()) {
187 bbox2 = otherItem->shieldHitBox(m_view);
188 } else {
189 bbox2 = otherItem->iconHitBox(m_view);
190 }
191
192 if (bbox.intersects(bbox2)) {
193 item->iconHidden = true;
194 break;
195 }
196 }
197 }
198 break;
199 case SceneGraphItemPayload::LabelPhase:
200 // place texts starting at the top (== back of m_renderBatch)
201 // and hide everything they would cover (unless overlap is explicitly allowed)
202 // TODO this doesn't seem to work for line following labels yet
203 // TODO ensure minimum repeat distance between texts here
204 for (auto it = m_renderBatch.rbegin(); it != m_renderBatch.rend(); ++it) {
205 if (((*it)->renderPhases() & SceneGraphItemPayload::LabelPhase) == 0) {
206 continue;
207 }
208 const auto item = dynamic_cast<LabelItem*>(*it);
209 if (!item) {
210 continue;
211 }
212 item->textHidden = false;
213 if (item->allowTextOverlap) {
214 continue;
215 }
216 if (item->iconHidden) {
217 item->textHidden = true; // if the icon is already hidden don't even bother trying text
218 continue;
219 }
220
221 const QRectF bbox = item->textHitBox(m_view);
222
223 // we need to search the full set here
224 // - icons/shields are already rendered, so we can collide with all of those
225 // - non-shield texts are being laid out, so we need to only look at things after a it.base() or later
226 for (auto it2 = m_renderBatch.begin(); it2 != m_renderBatch.end(); ++it2) {
227 if (it2 == std::prev(it.base())) {
228 continue;
229 }
230 const auto p = (*it2)->renderPhases();
231 if ((p & SceneGraphItemPayload::IconPhase) == 0 && ((p & SceneGraphItemPayload::LabelPhase) == 0 || it2 < it.base())) {
232 continue;
233 }
234
235 const auto otherItem = dynamic_cast<LabelItem*>((*it2));
236 if (!otherItem || otherItem->iconHidden || otherItem->allowTextOverlap) { // TODO limit the allowTextOverlap check to icons, this is a workaround for wrong z order
237 continue;
238 }
239
240 if (otherItem->hasShield()) {
241 if (otherItem->shieldHitBox(m_view).intersects(bbox)) {
242 item->textHidden = true;
243 break;
244 }
245 continue;
246 }
247 if (it2 >= it.base() && otherItem->hasText() && !otherItem->textHidden && otherItem->textHitBox(m_view).intersects(bbox)) {
248 item->textHidden = true;
249 break;
250 }
251 if (otherItem->hasIcon() && otherItem->iconHitBox(m_view).intersects(bbox)) {
252 item->textHidden = true;
253 break;
254 }
255 }
256 }
257 break;
258 }
259}
260
261static inline void drawGeometry(QPainter *painter, const QPolygonF &polygon) { painter->drawPolygon(polygon, Qt::OddEvenFill); }
262static inline void drawGeometry(QPainter *painter, const QPainterPath &path) { painter->drawPath(path); }
263
264template <typename T>
265inline void PainterRenderer::renderPolygonFill(PolygonBaseItem *item, const T &geom)
266{
267 if (item->fillBrush.style() != Qt::NoBrush) {
268 m_painter->setBrush(item->fillBrush);
269 drawGeometry(m_painter, geom);
270 }
271 if (item->textureBrush.style() != Qt::NoBrush) {
272 item->textureBrush.setTransform(brushTransform());
273 m_painter->setOpacity(item->textureBrush.color().alphaF());
274 m_painter->setBrush(item->textureBrush);
275 drawGeometry(m_painter, geom);
276 m_painter->setOpacity(1.0);
277 }
278}
279
280template <typename T>
281inline void PainterRenderer::renderPolygonCasing(PolygonBaseItem *item, const T &geom)
282{
283 auto p = item->casingPen;
284 p.setWidthF(mapToSceneWidth(item->casingPen.widthF(), item->casingPenWidthUnit));
285 m_painter->setPen(p);
286 drawGeometry(m_painter, geom);
287}
288
289template <typename T>
290inline void PainterRenderer::renderPolygonLine(PolygonBaseItem *item, const T &geom)
291{
292 auto p = item->pen;
293 p.setWidthF(mapToSceneWidth(item->pen.widthF(), item->penWidthUnit));
294 m_painter->setPen(p);
295 drawGeometry(m_painter, geom);
296}
297
298void PainterRenderer::renderPolygon(PolygonItem *item, SceneGraphItemPayload::RenderPhase phase)
299{
300 if (item->useCasingFillMode()) {
301 if (phase == SceneGraphItemPayload::CasingPhase) {
302 renderPolygonCasing(item, item->polygon);
303 } else if (phase == SceneGraphItemPayload::StrokePhase) {
304 m_painter->setPen(Qt::NoPen);
305 renderPolygonFill(item, item->polygon);
306 m_painter->setBrush(Qt::NoBrush);
307 }
308 } else {
309 if (phase == SceneGraphItemPayload::FillPhase) {
310 renderPolygonFill(item, item->polygon);
311 } else if (phase == SceneGraphItemPayload::StrokePhase) {
312 renderPolygonLine(item, item->polygon);
313 }
314 }
315}
316
317void PainterRenderer::renderMultiPolygon(MultiPolygonItem *item, SceneGraphItemPayload::RenderPhase phase)
318{
319 if (item->useCasingFillMode()) {
320 if (phase == SceneGraphItemPayload::CasingPhase) {
321 renderPolygonCasing(item, item->path);
322 } else if (phase == SceneGraphItemPayload::StrokePhase) {
323 m_painter->setPen(Qt::NoPen);
324 renderPolygonFill(item, item->path);
325 m_painter->setBrush(Qt::NoBrush);
326 }
327 } else {
328 if (phase == SceneGraphItemPayload::FillPhase) {
329 renderPolygonFill(item, item->path);
330 } else if (phase == SceneGraphItemPayload::StrokePhase) {
331 renderPolygonLine(item, item->path);
332 }
333 }
334}
335
336void PainterRenderer::renderPolyline(PolylineItem *item, SceneGraphItemPayload::RenderPhase phase)
337{
338 if (phase == SceneGraphItemPayload::StrokePhase) {
339 auto p = item->pen;
340 if (p.brush().style() == Qt::TexturePattern) {
341 m_painter->save();
342 const auto wt = m_painter->transform();
343 m_painter->resetTransform();
344 p.setWidthF(mapToScreenWidth(item->pen.widthF(), item->penWidthUnit));
345 auto b = p.brush();
346 Q_ASSERT(item->path.size() > 1);
347 for (auto it = item->path.constBegin(); it != std::prev(item->path.constEnd()); ++it) {
348 QLineF line(wt.map(*it), wt.map(*std::next(it)));
349 b.setTransform(QTransform().translate(wt.map(*it).x(), wt.map(*it).y()).rotate(-line.angle()).translate(0.0, -p.widthF() / 2.0));
350 p.setBrush(b);
351 m_painter->setPen(p);
352 m_painter->drawLine(line);
353 }
354 m_painter->restore();
355 } else {
356 p.setWidthF(mapToSceneWidth(item->pen.widthF(), item->penWidthUnit));
357 m_painter->setPen(p);
358 m_painter->drawPolyline(item->path);
359 }
360 } else {
361 auto p = item->casingPen;
362 p.setWidthF(mapToSceneWidth(item->pen.widthF(), item->penWidthUnit) + mapToSceneWidth(item->casingPen.widthF(), item->casingPenWidthUnit));
363 m_painter->setPen(p);
364 m_painter->drawPolyline(item->path);
365 }
366}
367
368void PainterRenderer::renderLabel(LabelItem *item, SceneGraphItemPayload::RenderPhase phase)
369{
370 m_painter->save();
371 m_painter->translate(m_view->mapSceneToScreen(item->pos));
372 m_painter->rotate(item->angle);
373
374 auto box = item->boundingRect(m_view);
375 box.moveCenter({0.0, 0.0});
376
377 // compute icon output size
378 QSizeF iconOutputSize = item->iconOutputSize(m_view);
379 if (!item->icon.isNull()) {
380 box.moveTop(-iconOutputSize.height() / 2.0);
381 }
382
383#if 0
384 m_painter->setPen(Qt::green);
385 m_painter->drawRect(item->iconHitBox(m_view).translated(-m_view->mapSceneToScreen(item->pos)));
386 m_painter->setPen(Qt::blue);
387 m_painter->drawRect(item->textHitBox(m_view).translated(-m_view->mapSceneToScreen(item->pos)));
388 m_painter->setPen(Qt::red);
389 m_painter->drawRect(item->shieldHitBox(m_view).translated(-m_view->mapSceneToScreen(item->pos)));
390#endif
391
392 // draw shield
393 // @see https://wiki.openstreetmap.org/wiki/MapCSS/0.2#Shield_properties
394 auto w = item->casingWidth + item->frameWidth + 2.0;
395 if (item->casingWidth > 0.0 && item->casingColor.alpha() > 0) {
396 m_painter->fillRect(box.adjusted(-w, -w, w, w), item->casingColor);
397 }
398 w -= item->casingWidth;
399 if (item->frameWidth > 0.0 && item->frameColor.alpha() > 0) {
400 m_painter->fillRect(box.adjusted(-w, -w, w, w), item->frameColor);
401 }
402 w -= item->frameWidth;
403 if (item->shieldColor.alpha() > 0) {
404 m_painter->fillRect(box.adjusted(-w, -w, w, w), item->shieldColor);
405 }
406
407 // draw icon
408 if (!iconOutputSize.isNull() && phase == SceneGraphItemPayload::IconPhase) {
409 QRectF iconRect(QPointF(-iconOutputSize.width() / 2.0, -iconOutputSize.height() / 2.0), iconOutputSize);
410 m_painter->setOpacity(item->iconOpacity);
411 item->icon.paint(m_painter, iconRect.toRect());
412 m_painter->setOpacity(1.0);
413 }
414 box.moveTop(box.top() + item->textOffset);
415
416 // center-align the text (item->text.size().width() != item->textOutputSize()...)
417 box.setWidth(item->text.size().width());
418 box.moveCenter({0.0, box.center().y()});
419
420 if (item->hasText() && (phase == SceneGraphItemPayload::LabelPhase || item->hasShield())) {
421 // draw text halo
422 if (item->haloRadius > 0.0 && item->haloColor.alphaF() > 0.0) {
423 const auto haloBox = box.adjusted(-item->haloRadius, -item->haloRadius, item->haloRadius, item->haloRadius);
424 QImage haloBuffer(haloBox.size().toSize(), QImage::Format_ARGB32);
425 haloBuffer.fill(Qt::transparent);
426 QPainter haloPainter(&haloBuffer);
427 haloPainter.setPen(item->haloColor);
428 haloPainter.setFont(item->font);
429 auto haloTextRect = box;
430 haloTextRect.moveTopLeft({item->haloRadius, item->haloRadius});
431 if (!item->isComplexText) {
432 haloPainter.drawStaticText(haloTextRect.topLeft(), item->text);
433 } else {
434 haloPainter.drawText(haloTextRect, item->text.text(), item->text.textOption());
435 }
436 StackBlur::blur(haloBuffer, item->haloRadius);
437 haloPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
438 haloPainter.fillRect(haloBuffer.rect(), item->haloColor);
439 m_painter->drawImage(haloBox, haloBuffer);
440 }
441
442 // draw text
443 m_painter->setPen(item->color);
444 m_painter->setFont(item->font);
445 if (!item->isComplexText) {
446 m_painter->drawStaticText(box.topLeft(), item->text);
447 } else {
448 m_painter->drawText(box, item->text.text(), item->text.textOption());
449 }
450 }
451
452 m_painter->restore();
453}
454
455void PainterRenderer::renderForeground(const QColor &bgColor)
456{
457 // fade out the map at the end of the scene box, to indicate you can't scroll further
458 m_painter->setTransform(m_view->deviceTransform());
459 m_painter->setClipRect(m_view->mapSceneToScreen(m_view->viewport()));
460 const auto borderWidth = 10;
461
462 QColor c(bgColor);
463 c.setAlphaF(0.75);
464 QLinearGradient gradient;
465 gradient.setColorAt(0, bgColor);
466 gradient.setColorAt(0.2, c);
467 gradient.setColorAt(1, Qt::transparent);
468
469 auto r = m_view->mapSceneToScreen(m_view->sceneBoundingBox());
470 r.setBottom(r.top() + borderWidth);
471 gradient.setStart(r.topLeft());
472 gradient.setFinalStop(r.bottomLeft());
473 m_painter->fillRect(r, gradient);
474
475 r = m_view->mapSceneToScreen(m_view->sceneBoundingBox());
476 r.setTop(r.bottom() - borderWidth);
477 gradient.setStart(r.bottomLeft());
478 gradient.setFinalStop(r.topLeft());
479 m_painter->fillRect(r, gradient);
480
481 r = m_view->mapSceneToScreen(m_view->sceneBoundingBox());
482 r.setRight(r.left() + borderWidth);
483 gradient.setStart(r.topLeft());
484 gradient.setFinalStop(r.topRight());
485 m_painter->fillRect(r, gradient);
486
487 r = m_view->mapSceneToScreen(m_view->sceneBoundingBox());
488 r.setLeft(r.right() - borderWidth);
489 gradient.setStart(r.topRight());
490 gradient.setFinalStop(r.topLeft());
491 m_painter->fillRect(r, gradient);
492}
493
494void PainterRenderer::endRender()
495{
496 m_painter->restore();
497}
498
499double PainterRenderer::mapToSceneWidth(double width, Unit unit) const
500{
501 switch (unit) {
502 case Unit::Pixel:
503 return m_view->mapScreenDistanceToSceneDistance(width);
504 case Unit::Meter:
505 return m_view->mapMetersToScene(width);
506 }
507
508 return width;
509}
510
511double PainterRenderer::mapToScreenWidth(double width, Unit unit) const
512{
513 switch (unit) {
514 case Unit::Pixel:
515 return width * m_view->deviceTransform().m11();
516 case Unit::Meter:
517 return m_view->mapMetersToScreen(width);
518 }
519
520 return width;
521}
522
523QTransform PainterRenderer::brushTransform() const
524{
525 // the following is the easy solution here and produces the best quality rendering
526 // m_painter->transform().inverted().translate(m_painter->transform().dx(), m_painter->transform().dy());
527 // the downside however is that it's extremely irritating during continuous scaling
528
529 // the following does basically the same as the above, but with discrete zoom levels
530 // at the expense of rendering quality
531 constexpr const auto TextureZoomSteps = 5.0;
532 auto viewport = m_view->viewportForZoom(std::round(m_view->zoomLevel() * TextureZoomSteps) / TextureZoomSteps,
533 QPointF(m_view->screenWidth() / 2.0, m_view->screenHeight() / 2.0));
534 QTransform t;
535 t.scale(viewport.width() / m_view->screenWidth(), viewport.height() / m_view->screenHeight());
536 t.translate(viewport.x(), viewport.y());
537 return t;
538
539 // TODO maybe the best approach would be the best of both worlds, the second approach
540 // during continuous zooming, the first one when at a fixed zoom level?
541}
A text or icon label.
QRectF boundingRect(const View *view) const override
Bounding box of this item in scene coordinates.
Multi-polygon item, used for polygons with "holes" in them.
Base item for filled polygons.
bool useCasingFillMode() const
Render like lines, ie casing and filling in the stroke phase, rather than the default.
A single filled polygon.
A path/way/line item in the scenegraph.
RenderPhase
See MapCSS spec: "Within a layer, first all fills are rendered, then all casings, then all strokes,...
Scene graph of the currently displayed level.
Definition scenegraph.h:29
QColor backgroundColor() const
Canvas background color.
QRectF viewportForZoom(double zoom, QPointF screenCenter) const
Computes the viewport for the given zoom level and screenCenter.
Definition view.cpp:123
QTransform deviceTransform() const
Device tranformation for manual high DPI scaling.
Definition view.cpp:307
Q_INVOKABLE double mapMetersToScreen(double meters) const
Returns how many pixels on screen represent the distance of meters with the current view transformati...
Definition view.cpp:263
double mapMetersToScene(double meters) const
Returns how many units in scene coordinate represent the distance of meters in the current view trans...
Definition view.cpp:257
int screenWidth() const
Screen-space sizes, ie the size of the on-screen area used for displaying.
Definition view.cpp:63
double mapScreenDistanceToSceneDistance(double distance) const
Converts a distance in screen coordinates to a distance in scene coordinates.
Definition view.cpp:191
QPointF mapSceneToScreen(QPointF scenePos) const
Converts a point in scene coordinates to screen coordinates.
Definition view.cpp:175
QRectF sceneBoundingBox() const
The bounding box of the scene.
Definition view.cpp:142
QTransform sceneToScreenTransform() const
The transformation to apply to scene coordinate to get to the view on screen.
Definition view.cpp:208
OSM-based multi-floor indoor maps for buildings.
Unit
Unit for geometry sizes.
const QColor & color() const const
void setTransform(const QTransform &matrix)
Qt::BrushStyle style() const const
int alpha() const const
float alphaF() const const
qint64 elapsed() const const
void setColorAt(qreal position, const QColor &color)
bool isNull() const const
void paint(QPainter *painter, const QRect &rect, Qt::Alignment alignment, Mode mode, State state) const const
void setFinalStop(const QPointF &stop)
void setStart(const QPointF &start)
const_iterator constBegin() const const
const_iterator constEnd() const const
qsizetype size() const const
CompositionMode_SourceIn
void drawImage(const QPoint &point, const QImage &image)
void drawLine(const QLine &line)
void drawPath(const QPainterPath &path)
void drawPolygon(const QPoint *points, int pointCount, Qt::FillRule fillRule)
void drawPolyline(const QPoint *points, int pointCount)
void drawRect(const QRect &rectangle)
void drawStaticText(const QPoint &topLeftPosition, const QStaticText &staticText)
void drawText(const QPoint &position, const QString &text)
void fillRect(const QRect &rectangle, QGradient::Preset preset)
void resetTransform()
void restore()
void rotate(qreal angle)
void save()
void setBrush(Qt::BrushStyle style)
void setClipRect(const QRect &rectangle, Qt::ClipOperation operation)
void setFont(const QFont &font)
void setOpacity(qreal opacity)
void setPen(Qt::PenStyle style)
void setRenderHint(RenderHint hint, bool on)
void setTransform(const QTransform &transform, bool combine)
const QTransform & transform() const const
void translate(const QPoint &offset)
void setWidthF(qreal width)
qreal widthF() const const
QRectF intersected(const QRectF &rectangle) const const
bool intersects(const QRectF &rectangle) const const
void moveCenter(const QPointF &position)
QRectF translated(const QPointF &offset) const const
qreal height() const const
bool isNull() const const
qreal width() const const
QSizeF size() const const
QString text() const const
QTextOption textOption() const const
OddEvenFill
qreal m11() const const
QTransform & rotate(qreal a, Qt::Axis axis)
QTransform & scale(qreal sx, qreal sy)
QTransform & translate(qreal dx, qreal dy)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:06:15 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.