Index: src/hiderectitem.h =================================================================== --- src/hiderectitem.h +++ src/hiderectitem.h @@ -37,6 +37,15 @@ { public: HideRectItem(); + + QRectF rect() const; + void setRect(const QRectF &rect); + + qreal devicePixelRatio() const; + void setDevicePixelRatio(qreal dpr); + +private: + qreal m_devicePixelRatio; }; } // NameSpace KSaneIface Index: src/hiderectitem.cpp =================================================================== --- src/hiderectitem.cpp +++ src/hiderectitem.cpp @@ -33,10 +33,32 @@ { HideRectItem::HideRectItem() + : m_devicePixelRatio(1.0) { setOpacity(0.4); setPen(Qt::NoPen); setBrush(Qt::black); } +QRectF HideRectItem::rect() const +{ + QRectF r = QGraphicsRectItem::rect(); + return QRectF(r.topLeft() * m_devicePixelRatio, r.size() * m_devicePixelRatio); +} + +void HideRectItem::setRect(const QRectF &rect) +{ + QGraphicsRectItem::setRect(QRectF(rect.topLeft() / m_devicePixelRatio, rect.size() / m_devicePixelRatio)); +} + +qreal HideRectItem::devicePixelRatio() const +{ + return m_devicePixelRatio; +} + +void HideRectItem::setDevicePixelRatio(qreal dpr) +{ + m_devicePixelRatio = dpr; +} + } // NameSpace KSaneIface Index: src/ksaneviewer.h =================================================================== --- src/ksaneviewer.h +++ src/ksaneviewer.h @@ -115,6 +115,8 @@ int refineRow(int fromRow, int toRow, int rowStart, int rowEnd); int refineColumn(int fromCol, int toCol, int colStart, int colEnd); + QPointF scenePos(QMouseEvent *e) const; + struct Private; Private *const d; Index: src/ksaneviewer.cpp =================================================================== --- src/ksaneviewer.cpp +++ src/ksaneviewer.cpp @@ -85,7 +85,8 @@ // Init the scene d->scene = new QGraphicsScene(this); - d->scene->setSceneRect(0, 0, img->width(), img->height()); + const auto dpr = img->devicePixelRatio(); + d->scene->setSceneRect(0, 0, img->width() / dpr, img->height() / dpr); setScene(d->scene); d->selection = new SelectionItem(QRectF()); @@ -141,7 +142,10 @@ void KSaneViewer::drawBackground(QPainter *painter, const QRectF &rect) { painter->fillRect(rect, QColor(0x70, 0x70, 0x70)); - painter->drawImage(rect, *d->img, rect); + QRectF r = rect & sceneRect(); + const qreal dpr = d->img->devicePixelRatio(); + QRectF srcRect = QRectF(r.topLeft() * dpr, r.size() * dpr); + painter->drawImage(r, *d->img, srcRect); } // ------------------------------------------------------------------------ @@ -166,9 +170,18 @@ // clear zoom setMatrix(QMatrix()); - d->scene->setSceneRect(0, 0, img->width(), img->height()); + const auto dpr = img->devicePixelRatio(); + d->scene->setSceneRect(0, 0, img->width() / dpr, img->height() / dpr); d->selection->setMaxRight(img->width()); d->selection->setMaxBottom(img->height()); + + d->selection->setDevicePixelRatio(dpr); + d->hideTop->setDevicePixelRatio(dpr); + d->hideBottom->setDevicePixelRatio(dpr); + d->hideRight->setDevicePixelRatio(dpr); + d->hideLeft->setDevicePixelRatio(dpr); + d->hideArea->setDevicePixelRatio(dpr); + d->img = img; } @@ -527,7 +540,7 @@ if (e->button() == Qt::LeftButton) { d->m_left_last_x = e->x(); d->m_left_last_y = e->y(); - QPointF scenePoint = mapToScene(e->pos()); + QPointF scenePoint = scenePos(e) * d->selection->devicePixelRatio(); d->lastSPoint = scenePoint; if (e->modifiers() != Qt::ControlModifier) { if (!d->selection->isVisible()) { @@ -556,7 +569,7 @@ clearActiveSelection(); } - QPointF scenePoint = mapToScene(e->pos()); + QPointF scenePoint = scenePos(e) * d->selection->devicePixelRatio(); for (int i = 0; i < d->selectionList.size(); i++) { if (d->selectionList[i]->intersects(scenePoint) == SelectionItem::AddRemove) { d->scene->removeItem(d->selectionList[i]); @@ -573,6 +586,7 @@ if (!removed && (d->selection->intersects(scenePoint) == SelectionItem::AddRemove)) { // add the current selection SelectionItem *tmp = new SelectionItem(d->selection->rect()); + tmp->setDevicePixelRatio(d->img->devicePixelRatio()); d->selectionList.push_back(tmp); d->selectionList.back()->setSaved(true); d->selectionList.back()->saveZoom(transform().m11()); @@ -604,7 +618,7 @@ // ------------------------------------------------------------------------ void KSaneViewer::mouseMoveEvent(QMouseEvent *e) { - QPointF scenePoint = mapToScene(e->pos()); + QPointF scenePoint = scenePos(e) * d->selection->devicePixelRatio(); if (e->buttons()&Qt::LeftButton) { if (e->modifiers() == Qt::ControlModifier) { @@ -913,6 +927,7 @@ float selArea = (float)(wSelEnd - wSelStart) * (float)(hSelEnd - hSelStart); if (selArea > (area * MIN_AREA_SIZE)) { SelectionItem *tmp = new SelectionItem(QRect(QPoint(x1, y1), QPoint(x2, y2))); + tmp->setDevicePixelRatio(d->img->devicePixelRatio()); d->selectionList.push_back(tmp); d->selectionList.back()->setSaved(true); d->selectionList.back()->saveZoom(transform().m11()); @@ -1142,4 +1157,16 @@ return col; } +QPointF KSaneViewer::scenePos(QMouseEvent *e) const +{ + // QGraphicsView::mapToScene() maps only QPoints, but in highdpi mode we want + // to deal with non-rounded coordinates, that's why QPainterPath wrapper is used. + // QMouseEvent::localPos() currently returns a rounded QPointF + // (https://codereview.qt-project.org/259785), so we have to extract a fractional + // part from QMouseEvent::screenPos(). + const QPointF screenPos = e->screenPos(); + QPointF delta = screenPos - screenPos.toPoint(); + return mapToScene(QPainterPath(e->pos() + delta)).currentPosition(); +} + } // NameSpace KSaneIface Index: src/ksanewidget_p.cpp =================================================================== --- src/ksanewidget_p.cpp +++ src/ksanewidget_p.cpp @@ -715,7 +715,9 @@ x = (int)(SCALED_PREVIEW_MAX_SIDE / ratio); } - m_previewImg = QImage(x, y, QImage::Format_RGB32); + const qreal dpr = q->devicePixelRatioF(); + m_previewImg = QImage(QSize(x, y) * dpr, QImage::Format_RGB32); + m_previewImg.setDevicePixelRatio(dpr); m_previewImg.fill(0xFFFFFFFF); // set the new image Index: src/selectionitem.h =================================================================== --- src/selectionitem.h +++ src/selectionitem.h @@ -65,6 +65,9 @@ QPointF fixTranslation( QPointF dp); QRectF rect(); + qreal devicePixelRatio() const; + void setDevicePixelRatio(qreal dpr); + public: // Graphics Item methods QRectF boundingRect() const override; Index: src/selectionitem.cpp =================================================================== --- src/selectionitem.cpp +++ src/selectionitem.cpp @@ -54,6 +54,7 @@ qreal invZoom; qreal selMargin; QRectF addRemRect; + qreal devicePixelRatio; }; SelectionItem::SelectionItem(const QRectF &rect) : QGraphicsItem(), d(new Private) @@ -65,8 +66,10 @@ d->penDark.setColor(Qt::black); d->penDark.setStyle(Qt::SolidLine); + d->penDark.setWidth(0); d->penLight.setColor(Qt::white); d->penLight.setStyle(Qt::DashLine); + d->penLight.setWidth(0); // FIXME We should probably use some standard KDE color here and not hard code it d->penAddRemFg.setColor(Qt::darkGreen); @@ -79,6 +82,8 @@ d->selMargin = selMargin; d->addRemRect = QRectF(0, 0, 0, 0); + + d->devicePixelRatio = 1.0; } SelectionItem::~SelectionItem() @@ -97,7 +102,7 @@ qreal margin = addRemMargin * d->invZoom; QPointF pMargin = addRemMarginPoint * d->invZoom; - d->addRemRect = QRectF(d->rect.center() - pMargin, QSizeF(margin * 2.0, margin * 2.0)); + d->addRemRect = QRectF(d->rect.center() / d->devicePixelRatio - pMargin, QSizeF(margin * 2.0, margin * 2.0)); d->penAddRemFg.setWidthF(3.0 * d->invZoom); } @@ -193,10 +198,7 @@ update(); } - if ((point.x() > d->addRemRect.left()) && - (point.x() < d->addRemRect.right()) && - (point.y() > d->addRemRect.top()) && - (point.y() < d->addRemRect.bottom())) { + if (d->addRemRect.contains(point / d->devicePixelRatio)) { return AddRemove; } return Move; @@ -225,7 +227,7 @@ // calculate the add/remove rectangle qreal margin = addRemMargin * d->invZoom; QPointF pMargin = addRemMarginPoint * d->invZoom; - d->addRemRect = QRectF(d->rect.center() - pMargin, QSizeF(margin * 2, margin * 2)); + d->addRemRect = QRectF(d->rect.center() / d->devicePixelRatio - pMargin, QSizeF(margin * 2, margin * 2)); } QPointF SelectionItem::fixTranslation(QPointF dp) @@ -250,46 +252,45 @@ return d->rect; } -QRectF SelectionItem::boundingRect() const +qreal SelectionItem::devicePixelRatio() const { - QRectF tmp(d->rect.topLeft() - boundMargin, d->rect.bottomRight() + boundMargin); - if (tmp.top() > d->addRemRect.top()) { - tmp.setTop(d->addRemRect.top()); - } - if (tmp.left() > d->addRemRect.left()) { - tmp.setLeft(d->addRemRect.left()); - } - - if (tmp.bottom() < d->addRemRect.bottom()) { - tmp.setBottom(d->addRemRect.bottom()); - } + return d->devicePixelRatio; +} - if (tmp.right() < d->addRemRect.right()) { - tmp.setRight(d->addRemRect.right()); - } +void SelectionItem::setDevicePixelRatio(qreal dpr) +{ + d->devicePixelRatio = dpr; +} - return tmp; +QRectF SelectionItem::boundingRect() const +{ + const auto dpr = d->devicePixelRatio; + QRectF tmp(d->rect.topLeft() / dpr - boundMargin, d->rect.bottomRight() / dpr + boundMargin); + return tmp.united(d->addRemRect); } void SelectionItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) { + const auto dpr = d->devicePixelRatio; + QRectF rect(d->rect.topLeft() / dpr, d->rect.size() / dpr); + painter->setPen(d->penDark); - painter->drawRect(d->rect); + painter->drawRect(rect); painter->setPen(d->penLight); - painter->drawRect(d->rect); + painter->drawRect(rect); if (d->showAddRem) { painter->fillRect(d->addRemRect, QBrush(Qt::white)); - QLineF minus(d->addRemRect.left() + 3 * d->invZoom, d->rect.center().y(), - d->addRemRect.right() - 3 * d->invZoom, d->rect.center().y()); + QLineF minus(d->addRemRect.left() + 3 * d->invZoom, d->addRemRect.center().y(), + d->addRemRect.right() - 3 * d->invZoom, d->addRemRect.center().y()); painter->setPen(d->penAddRemFg); painter->drawLine(minus); if (!d->isSaved) { - QLineF plus(d->rect.center().x(), d->addRemRect.top() + 3 * d->invZoom, - d->rect.center().x(), d->addRemRect.bottom() - 3 * d->invZoom); + QLineF plus(d->addRemRect.center().x(), d->addRemRect.top() + 3 * d->invZoom, + d->addRemRect.center().x(), d->addRemRect.bottom() - 3 * d->invZoom); painter->drawLine(plus); } }