7#include "painterrenderer.h" 
    8#include "stackblur_p.h" 
    9#include "render-logging.h" 
   11#include <KOSMIndoorMap/SceneGraph> 
   12#include <KOSMIndoorMap/View> 
   15#include <QElapsedTimer> 
   16#include <QFontMetricsF> 
   17#include <QGuiApplication> 
   19#include <QLinearGradient> 
   26PainterRenderer::PainterRenderer() = 
default;
 
   27PainterRenderer::~PainterRenderer() = 
default;
 
   29void PainterRenderer::setPainter(
QPainter *painter)
 
   36    QElapsedTimer frameTimer;
 
   43    for (
const auto &layerOffsets : sg.layerOffsets()) {
 
   44        const auto layerBegin = sg.itemsBegin(layerOffsets);
 
   45        const auto layerEnd = sg.itemsEnd(layerOffsets);
 
   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());
 
   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());
 
   65        for (
auto phase : {SceneGraphItemPayload::FillPhase, SceneGraphItemPayload::CasingPhase, SceneGraphItemPayload::StrokePhase, SceneGraphItemPayload::IconPhase, SceneGraphItemPayload::LabelPhase}) {
 
   68            for (
auto it = m_renderBatch.begin(); it != m_renderBatch.end(); ++it) {
 
   69                const auto &item = (*it);
 
   70                if ((item->renderPhases() & phase) == 0) {
 
   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)) {
 
   82                    if (phase == SceneGraphItemPayload::IconPhase) {
 
   84                            renderLabel(i, phase);
 
   86                    } 
else if (phase == SceneGraphItemPayload::LabelPhase) {
 
   88                            renderLabel(i, phase);
 
   92                    qCritical() << 
"Unsupported scene graph item!";
 
  102    qCDebug(RenderLog) << 
"rendering took:" << frameTimer.
elapsed() << 
"ms for" << sg.items().size() << 
"items on" << sg.layerOffsets().size() << 
"layers";
 
  105void PainterRenderer::beginRender()
 
  110void PainterRenderer::renderBackground(
const QColor &bgColor)
 
  112    m_painter->setTransform(m_view->deviceTransform());
 
  113    m_painter->fillRect(0, 0, m_view->screenWidth(), m_view->screenHeight(), bgColor);
 
  119        case SceneGraphItemPayload::NoPhase:
 
  121        case SceneGraphItemPayload::FillPhase:
 
  123            m_painter->setTransform(m_view->sceneToScreenTransform() * m_view->deviceTransform());
 
  124            m_painter->setClipRect(m_view->viewport().intersected(m_view->sceneBoundingBox()));
 
  127        case SceneGraphItemPayload::CasingPhase:
 
  128        case SceneGraphItemPayload::StrokePhase:
 
  130            m_painter->setTransform(m_view->sceneToScreenTransform() * m_view->deviceTransform());
 
  131            m_painter->setClipRect(m_view->viewport().intersected(m_view->sceneBoundingBox()));
 
  134        case SceneGraphItemPayload::IconPhase:
 
  135        case SceneGraphItemPayload::LabelPhase:
 
  136            m_painter->setTransform(m_view->deviceTransform());
 
  146        case SceneGraphItemPayload::NoPhase:
 
  148        case SceneGraphItemPayload::FillPhase:
 
  149        case SceneGraphItemPayload::CasingPhase:
 
  150        case SceneGraphItemPayload::StrokePhase:
 
  152        case SceneGraphItemPayload::IconPhase:
 
  156            for (
auto it = m_renderBatch.rbegin(); it != m_renderBatch.rend(); ++it) {
 
  157                if (((*it)->renderPhases() & SceneGraphItemPayload::IconPhase) == 0) {
 
  160                const auto item = 
dynamic_cast<LabelItem*
>(*it);
 
  164                item->iconHidden = 
false;
 
  165                if (item->allowIconOverlap) {
 
  170                if (item->hasShield()) {
 
  171                    bbox = item->shieldHitBox(m_view);
 
  173                    bbox = item->iconHitBox(m_view);
 
  176                for (
auto it2 = it.base(); it2 != m_renderBatch.end(); ++it2) {
 
  177                    if (((*it2)->renderPhases() & SceneGraphItemPayload::IconPhase) == 0) {
 
  180                    const auto otherItem = 
dynamic_cast<LabelItem*
>((*it2));
 
  181                    if (!otherItem || otherItem->allowIconOverlap) { 
 
  186                    if (otherItem->hasShield()) {
 
  187                        bbox2 = otherItem->shieldHitBox(m_view);
 
  189                        bbox2 = otherItem->iconHitBox(m_view);
 
  193                        item->iconHidden = 
true;
 
  199        case SceneGraphItemPayload::LabelPhase:
 
  204            for (
auto it = m_renderBatch.rbegin(); it != m_renderBatch.rend(); ++it) {
 
  205                if (((*it)->renderPhases() & SceneGraphItemPayload::LabelPhase) == 0) {
 
  208                const auto item = 
dynamic_cast<LabelItem*
>(*it);
 
  212                item->textHidden = 
false;
 
  213                if (item->allowTextOverlap) {
 
  216                if (item->iconHidden) {
 
  217                    item->textHidden = 
true; 
 
  221                const QRectF bbox = item->textHitBox(m_view);
 
  226                for (
auto it2 = m_renderBatch.begin(); it2 != m_renderBatch.end(); ++it2) {
 
  227                    if (it2 == std::prev(it.base())) {
 
  230                    const auto p = (*it2)->renderPhases();
 
  231                    if ((p & SceneGraphItemPayload::IconPhase) == 0 && ((p & SceneGraphItemPayload::LabelPhase) == 0 || it2 < it.base())) {
 
  235                    const auto otherItem = 
dynamic_cast<LabelItem*
>((*it2));
 
  236                    if (!otherItem || otherItem->iconHidden || otherItem->allowTextOverlap) { 
 
  240                    if (otherItem->hasShield()) {
 
  241                        if (otherItem->shieldHitBox(m_view).intersects(bbox)) {
 
  242                            item->textHidden = 
true;
 
  247                    if (it2 >= it.base() && otherItem->hasText() && !otherItem->textHidden && otherItem->textHitBox(m_view).intersects(bbox)) {
 
  248                        item->textHidden = 
true;
 
  251                    if (otherItem->hasIcon() && otherItem->iconHitBox(m_view).intersects(bbox)) {
 
  252                        item->textHidden = 
true;
 
  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); }
 
  265inline void PainterRenderer::renderPolygonFill(
PolygonBaseItem *item, 
const T &geom)
 
  268        m_painter->setBrush(item->fillBrush);
 
  269        drawGeometry(m_painter, geom);
 
  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);
 
  281inline void PainterRenderer::renderPolygonCasing(
PolygonBaseItem *item, 
const T &geom)
 
  283    auto p = item->casingPen;
 
  284    p.
setWidthF(mapToSceneWidth(item->casingPen.
widthF(), item->casingPenWidthUnit));
 
  285    m_painter->setPen(p);
 
  286    drawGeometry(m_painter, geom);
 
  290inline void PainterRenderer::renderPolygonLine(
PolygonBaseItem *item, 
const T &geom)
 
  294    m_painter->setPen(p);
 
  295    drawGeometry(m_painter, geom);
 
  301        if (phase == SceneGraphItemPayload::CasingPhase) {
 
  302            renderPolygonCasing(item, item->polygon);
 
  303        } 
else if (phase == SceneGraphItemPayload::StrokePhase) {
 
  305            renderPolygonFill(item, item->polygon);
 
  309        if (phase == SceneGraphItemPayload::FillPhase) {
 
  310            renderPolygonFill(item, item->polygon);
 
  311        } 
else if (phase == SceneGraphItemPayload::StrokePhase) {
 
  312            renderPolygonLine(item, item->polygon);
 
  320        if (phase == SceneGraphItemPayload::CasingPhase) {
 
  321            renderPolygonCasing(item, item->path);
 
  322        } 
else if (phase == SceneGraphItemPayload::StrokePhase) {
 
  324            renderPolygonFill(item, item->path);
 
  328        if (phase == SceneGraphItemPayload::FillPhase) {
 
  329            renderPolygonFill(item, item->path);
 
  330        } 
else if (phase == SceneGraphItemPayload::StrokePhase) {
 
  331            renderPolygonLine(item, item->path);
 
  338    if (phase == SceneGraphItemPayload::StrokePhase) {
 
  342            const auto wt = m_painter->transform();
 
  343            m_painter->resetTransform();
 
  344            p.setWidthF(mapToScreenWidth(item->pen.
widthF(), item->penWidthUnit));
 
  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));
 
  351                m_painter->setPen(p);
 
  352                m_painter->drawLine(line);
 
  354            m_painter->restore();
 
  356            p.setWidthF(mapToSceneWidth(item->pen.
widthF(), item->penWidthUnit));
 
  357            m_painter->setPen(p);
 
  358            m_painter->drawPolyline(item->path);
 
  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);
 
  371    m_painter->translate(m_view->mapSceneToScreen(item->pos));
 
  372    m_painter->rotate(item->angle);
 
  378    QSizeF iconOutputSize = item->iconOutputSize(m_view);
 
  379    if (!item->icon.
isNull()) {
 
  380        box.moveTop(-iconOutputSize.
height() / 2.0);
 
  385    m_painter->drawRect(item->iconHitBox(m_view).
translated(-m_view->mapSceneToScreen(item->pos)));
 
  387    m_painter->drawRect(item->textHitBox(m_view).
translated(-m_view->mapSceneToScreen(item->pos)));
 
  389    m_painter->drawRect(item->shieldHitBox(m_view).
translated(-m_view->mapSceneToScreen(item->pos)));
 
  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);
 
  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);
 
  402    w -= item->frameWidth;
 
  403    if (item->shieldColor.
alpha() > 0) {
 
  404        m_painter->fillRect(box.adjusted(-w, -w, w, w), item->shieldColor);
 
  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);
 
  414    box.moveTop(box.top() + item->textOffset);
 
  418    box.moveCenter({0.0, box.center().y()});
 
  420    if (item->hasText() && (phase == SceneGraphItemPayload::LabelPhase || item->hasShield())) {
 
  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);
 
  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);
 
  434                haloPainter.drawText(haloTextRect, item->text.
text(), item->text.
textOption());
 
  436            StackBlur::blur(haloBuffer, item->haloRadius);
 
  438            haloPainter.fillRect(haloBuffer.rect(), item->haloColor);
 
  439            m_painter->drawImage(haloBox, haloBuffer);
 
  443        m_painter->setPen(item->color);
 
  444        m_painter->setFont(item->font);
 
  445        if (!item->isComplexText) {
 
  446            m_painter->drawStaticText(box.topLeft(), item->text);
 
  448            m_painter->drawText(box, item->text.
text(), item->text.
textOption());
 
  452    m_painter->restore();
 
  455void PainterRenderer::renderForeground(
const QColor &bgColor)
 
  458    m_painter->setTransform(m_view->deviceTransform());
 
  459    m_painter->setClipRect(m_view->mapSceneToScreen(m_view->viewport()));
 
  460    const auto borderWidth = 10;
 
  464    QLinearGradient gradient;
 
  469    auto r = m_view->mapSceneToScreen(m_view->sceneBoundingBox());
 
  470    r.setBottom(r.top() + borderWidth);
 
  473    m_painter->fillRect(r, gradient);
 
  475    r = m_view->mapSceneToScreen(m_view->sceneBoundingBox());
 
  476    r.setTop(r.bottom() - borderWidth);
 
  479    m_painter->fillRect(r, gradient);
 
  481    r = m_view->mapSceneToScreen(m_view->sceneBoundingBox());
 
  482    r.setRight(r.left() + borderWidth);
 
  485    m_painter->fillRect(r, gradient);
 
  487    r = m_view->mapSceneToScreen(m_view->sceneBoundingBox());
 
  488    r.setLeft(r.right() - borderWidth);
 
  491    m_painter->fillRect(r, gradient);
 
  494void PainterRenderer::endRender()
 
  496    m_painter->restore();
 
  499double PainterRenderer::mapToSceneWidth(
double width, 
Unit unit)
 const 
  503            return m_view->mapScreenDistanceToSceneDistance(width);
 
  505            return m_view->mapMetersToScene(width);
 
  511double PainterRenderer::mapToScreenWidth(
double width, 
Unit unit)
 const 
  515            return width * m_view->deviceTransform().m11();
 
  517            return m_view->mapMetersToScreen(width);
 
  523QTransform PainterRenderer::brushTransform()
 const 
  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));
 
  535    t.
scale(viewport.width() / m_view->screenWidth(), viewport.height() / m_view->screenHeight());
 
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 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.
 
QColor backgroundColor() const
Canvas background color.
 
View transformations and transformation manipulation.
 
OSM-based multi-floor indoor maps for buildings.
 
Unit
Unit for geometry sizes.
 
const QColor & color() const const
 
Qt::BrushStyle style() 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
 
void drawPath(const QPainterPath &path)
 
void drawPolygon(const QPoint *points, int pointCount, Qt::FillRule fillRule)
 
void setWidthF(qreal width)
 
qreal widthF() 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