diff --git a/lib/document/document.h b/lib/document/document.h --- a/lib/document/document.h +++ b/lib/document/document.h @@ -133,13 +133,35 @@ void waitUntilLoaded(); + /** + * Returns the size of the loaded document + * This value is NOT adjusted for HiDPI rendering - + * meaning that the real size of the image will be returned + */ QSize size() const; + /** + * Returns the size of the loaded document + * This value is adjusted for HiDPI rendering - + * meaning that the scaled size of the image will be returned + */ + QSize scaledSize() const; + + /** + * Returns the width of the loaded document + * This value is NOT adjusted for HiDPI rendering - + * meaning that the real size of the image will be returned + */ int width() const { return size().width(); } + /** + * Returns the height of the loaded document + * This value is NOT adjusted for HiDPI rendering - + * meaning that the real size of the image will be returned + */ int height() const { return size().height(); diff --git a/lib/document/document.cpp b/lib/document/document.cpp --- a/lib/document/document.cpp +++ b/lib/document/document.cpp @@ -352,6 +352,11 @@ return d->mSize; } +QSize Document::scaledSize() const +{ + return d->mSize / qApp->devicePixelRatio(); +} + bool Document::hasAlphaChannel() const { if (d->mImage.isNull()) { diff --git a/lib/document/documentloadedimpl.cpp b/lib/document/documentloadedimpl.cpp --- a/lib/document/documentloadedimpl.cpp +++ b/lib/document/documentloadedimpl.cpp @@ -28,6 +28,7 @@ #include #include #include +#include // KDE @@ -63,7 +64,12 @@ void DocumentLoadedImpl::init() { if (!d->mQuietInit) { - emit imageRectUpdated(document()->image().rect()); + const qreal dpr = qApp->devicePixelRatio(); + QRect imageRect = QRectF(document()->image().rect().x() / dpr, + document()->image().rect().y() / dpr, + document()->image().rect().width() / dpr, + document()->image().rect().height() / dpr).toAlignedRect(); + emit imageRectUpdated(imageRect); emit loaded(); } } @@ -103,7 +109,12 @@ void DocumentLoadedImpl::setImage(const QImage& image) { setDocumentImage(image); - imageRectUpdated(image.rect()); + const qreal dpr = qApp->devicePixelRatio(); + QRect imageRect = QRectF(image.rect().x() / dpr, + image.rect().y() / dpr, + image.rect().width() / dpr, + image.rect().height() / dpr).toAlignedRect(); + imageRectUpdated(imageRect); } void DocumentLoadedImpl::applyTransformation(Orientation orientation) @@ -112,7 +123,12 @@ QMatrix matrix = ImageUtils::transformMatrix(orientation); image = image.transformed(matrix); setDocumentImage(image); - imageRectUpdated(image.rect()); + const qreal dpr = qApp->devicePixelRatio(); + QRect imageRect = QRectF(image.rect().x() / dpr, + image.rect().y() / dpr, + image.rect().width() / dpr, + image.rect().height() / dpr).toAlignedRect(); + imageRectUpdated(imageRect); } QByteArray DocumentLoadedImpl::rawData() const diff --git a/lib/documentview/abstractimageview.h b/lib/documentview/abstractimageview.h --- a/lib/documentview/abstractimageview.h +++ b/lib/documentview/abstractimageview.h @@ -67,14 +67,31 @@ qreal computeZoomToFitWidth() const; + /** + * Returns the size of the loaded document + * This value is NOT adjusted for HiDPI rendering - + * meaning that the real size of the image will be returned + */ QSizeF documentSize() const; + /** + * Returns the size of the loaded document + * This value is adjusted for HiDPI rendering - + * meaning that the scaled size of the image will be returned + */ + QSizeF scaledDocumentSize() const; + + /* + * The size of the image that is currently visible, + * in HiDPI-scaled coordinates + */ QSizeF visibleImageSize() const; /** * If the image is smaller than the view, imageOffset is the distance from * the topleft corner of the view to the topleft corner of the image. * Neither x nor y can be negative. + * This is in image coordinats and NOT adjusted for HiDPI rendering. */ QPointF imageOffset() const; diff --git a/lib/documentview/abstractimageview.cpp b/lib/documentview/abstractimageview.cpp --- a/lib/documentview/abstractimageview.cpp +++ b/lib/documentview/abstractimageview.cpp @@ -30,6 +30,7 @@ #include #include #include +#include namespace Gwenview { @@ -58,7 +59,7 @@ void adjustImageOffset(Verbosity verbosity = Notify) { - QSizeF zoomedDocSize = q->documentSize() * mZoom; + QSizeF zoomedDocSize = q->scaledDocumentSize() * mZoom; QSizeF viewSize = q->boundingRect().size(); QPointF offset( qMax((viewSize.width() - zoomedDocSize.width()) / 2, qreal(0.)), @@ -83,7 +84,7 @@ mScrollPos = _newPos; return; } - QSizeF zoomedDocSize = q->documentSize() * mZoom; + QSizeF zoomedDocSize = q->scaledDocumentSize() * mZoom; QSizeF viewSize = q->boundingRect().size(); QPointF newPos( qBound(qreal(0.), _newPos.x(), zoomedDocSize.width() - viewSize.width()), @@ -157,6 +158,11 @@ return d->mDocument ? d->mDocument->size() : QSizeF(); } +QSizeF AbstractImageView::scaledDocumentSize() const +{ + return d->mDocument ? d->mDocument->scaledSize() : QSizeF(); +} + qreal AbstractImageView::zoom() const { return d->mZoom; @@ -213,7 +219,7 @@ of the new offset is not 0, we won't be able to center on a specific point in that direction. */ - QPointF oldScroll = scrollPos() - imageOffset(); + QPointF oldScroll = scrollPos() - (imageOffset() / qApp->devicePixelRatio()); QPointF scroll = (zoom / oldZoom) * (oldScroll + center) - center; @@ -285,7 +291,7 @@ qreal AbstractImageView::computeZoomToFit() const { - QSizeF docSize = documentSize(); + QSizeF docSize = scaledDocumentSize(); if (docSize.isEmpty()) { return 1; } @@ -301,7 +307,7 @@ qreal AbstractImageView::computeZoomToFitWidth() const { - QSizeF docSize = documentSize(); + QSizeF docSize = scaledDocumentSize(); if (docSize.isEmpty()) { return 1; } @@ -444,7 +450,7 @@ d->setScrollPos(QPointF(d->mScrollPos.x(), 0)); return; case Qt::Key_End: - d->setScrollPos(QPointF(d->mScrollPos.x(), documentSize().height() * zoom())); + d->setScrollPos(QPointF(d->mScrollPos.x(), scaledDocumentSize().height() * zoom())); return; default: return; @@ -484,7 +490,7 @@ QPointF AbstractImageView::mapToView(const QPointF& imagePos) const { - return imagePos * d->mZoom + d->mImageOffset - d->mScrollPos; + return (imagePos / qApp->devicePixelRatio()) * d->mZoom + (d->mImageOffset) - d->mScrollPos; } QPoint AbstractImageView::mapToView(const QPoint& imagePos) const @@ -496,7 +502,7 @@ { return QRectF( mapToView(imageRect.topLeft()), - imageRect.size() * zoom() + imageRect.size() * zoom() / qApp->devicePixelRatio() ); } @@ -504,13 +510,13 @@ { return QRect( mapToView(imageRect.topLeft()), - imageRect.size() * zoom() + imageRect.size() / qApp->devicePixelRatio() * zoom() ); } QPointF AbstractImageView::mapToImage(const QPointF& viewPos) const { - return (viewPos - d->mImageOffset + d->mScrollPos) / d->mZoom; + return (viewPos - d->mImageOffset + d->mScrollPos) / d->mZoom * qApp->devicePixelRatio(); } QPoint AbstractImageView::mapToImage(const QPoint& viewPos) const @@ -522,7 +528,7 @@ { return QRectF( mapToImage(viewRect.topLeft()), - viewRect.size() / zoom() + viewRect.size() / zoom() * qApp->devicePixelRatio() ); } @@ -530,7 +536,7 @@ { return QRect( mapToImage(viewRect.topLeft()), - viewRect.size() / zoom() + viewRect.size() / zoom() * qApp->devicePixelRatio() ); } @@ -560,7 +566,7 @@ if (!document()) { return QSizeF(); } - QSizeF size = documentSize() * zoom(); + QSizeF size = scaledDocumentSize() * zoom(); return size.boundedTo(boundingRect().size()); } diff --git a/lib/documentview/birdeyeview.cpp b/lib/documentview/birdeyeview.cpp --- a/lib/documentview/birdeyeview.cpp +++ b/lib/documentview/birdeyeview.cpp @@ -138,7 +138,7 @@ if (!d->mDocView->canZoom() || d->mDocView->zoomToFit()) { return; } - QSizeF size = d->mDocView->document()->size(); + QSizeF size = d->mDocView->document()->scaledSize(); size.scale(MIN_SIZE, MIN_SIZE, Qt::KeepAspectRatioByExpanding); QRectF docViewRect = d->mDocView->boundingRect(); int maxBevHeight = docViewRect.height() - 2 * VIEW_OFFSET; @@ -163,7 +163,7 @@ void BirdEyeView::adjustVisibleRect() { - QSizeF docSize = d->mDocView->document()->size(); + QSizeF docSize = d->mDocView->document()->scaledSize(); qreal viewZoom = d->mDocView->zoom(); qreal bevZoom; if (docSize.height() > docSize.width()) { diff --git a/lib/documentview/rasterimageview.cpp b/lib/documentview/rasterimageview.cpp --- a/lib/documentview/rasterimageview.cpp +++ b/lib/documentview/rasterimageview.cpp @@ -35,6 +35,7 @@ #include #include #include +#include // LCMS2 #include @@ -140,7 +141,7 @@ QRectF mapViewportToZoomedImage(const QRectF& viewportRect) const { return QRectF( - viewportRect.topLeft() - q->imageOffset() + q->scrollPos(), + viewportRect.topLeft() - (q->imageOffset() / qApp->devicePixelRatio()) + q->scrollPos(), viewportRect.size() ); } @@ -163,7 +164,8 @@ return; } - mAlternateBuffer = QPixmap(size); + mAlternateBuffer = QPixmap(size * qApp->devicePixelRatio()); + mAlternateBuffer.setDevicePixelRatio(qApp->devicePixelRatio()); mAlternateBuffer.fill(Qt::transparent); { QPainter painter(&mAlternateBuffer); @@ -377,6 +379,7 @@ { if (d->mAlternateBuffer.size() != d->mCurrentBuffer.size()) { d->mAlternateBuffer = QPixmap(d->mCurrentBuffer.size()); + d->mAlternateBuffer.setDevicePixelRatio(d->mCurrentBuffer.devicePixelRatio()); } QPainter painter(&d->mAlternateBuffer); painter.drawPixmap(-delta, d->mCurrentBuffer); @@ -384,7 +387,12 @@ qSwap(d->mCurrentBuffer, d->mAlternateBuffer); // Scale missing parts - QRegion bufferRegion = QRegion(d->mCurrentBuffer.rect().translated(scrollPos().toPoint())); + const qreal dpr = qApp->devicePixelRatio(); + QRect scaledCurrentBufferRect(QRectF(d->mCurrentBuffer.rect().x() / dpr, + d->mCurrentBuffer.rect().y() / dpr, + d->mCurrentBuffer.rect().width() / dpr, + d->mCurrentBuffer.rect().height() / dpr).toAlignedRect()); + QRegion bufferRegion = QRegion(scaledCurrentBufferRect.translated(scrollPos().toPoint())); QRegion updateRegion = bufferRegion - bufferRegion.translated(-delta.toPoint()); updateBuffer(updateRegion); update(); @@ -392,12 +400,14 @@ void RasterImageView::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/) { + d->mCurrentBuffer.setDevicePixelRatio(qApp->devicePixelRatio()); + QPointF topLeft = imageOffset(); if (zoomToFit()) { // In zoomToFit mode, scale crudely the buffer to fit the screen. This // provide an approximate rendered which will be replaced when the scheduled // proper scale is ready. - QSizeF size = documentSize() * zoom(); + QSizeF size = scaledDocumentSize() * zoom(); painter->drawPixmap(topLeft.x(), topLeft.y(), size.width(), size.height(), d->mCurrentBuffer); } else { painter->drawPixmap(topLeft, d->mCurrentBuffer); diff --git a/lib/imagescaler.cpp b/lib/imagescaler.cpp --- a/lib/imagescaler.cpp +++ b/lib/imagescaler.cpp @@ -23,6 +23,7 @@ #include #include #include +#include // KDE @@ -126,9 +127,13 @@ void ImageScaler::scaleRect(const QRect& rect) { + const qreal dpr = qApp->devicePixelRatio(); + const QRect dRect = QRectF(rect.x() * dpr, rect.y() * dpr, rect.width() * dpr, rect.height() * dpr).toAlignedRect(); + const qreal REAL_DELTA = 0.001; if (qAbs(d->mZoom - 1.0) < REAL_DELTA) { - QImage tmp = d->mDocument->image().copy(rect); + QImage tmp = d->mDocument->image().copy(dRect); + tmp.setDevicePixelRatio(dpr); scaledRect(rect.left(), rect.top(), tmp); return; } @@ -144,6 +149,10 @@ image = d->mDocument->image(); zoom = d->mZoom; } + const QRect imageRect = QRectF(image.rect().x() / dpr, + image.rect().y() / dpr, + image.rect().width() / dpr, + image.rect().height() / dpr).toAlignedRect(); // If rect contains "half" pixels, make sure sourceRect includes them QRectF sourceRectF( rect.left() / zoom, @@ -151,7 +160,7 @@ rect.width() / zoom, rect.height() / zoom); - sourceRectF = sourceRectF.intersected(image.rect()); + sourceRectF = sourceRectF.intersected(imageRect); QRect sourceRect = PaintUtils::containingRect(sourceRectF); if (sourceRect.isEmpty()) { return; @@ -165,8 +174,8 @@ if (needsSmoothMargins) { sourceLeftMargin = qMin(sourceRect.left(), SMOOTH_MARGIN); sourceTopMargin = qMin(sourceRect.top(), SMOOTH_MARGIN); - sourceRightMargin = qMin(image.rect().right() - sourceRect.right(), SMOOTH_MARGIN); - sourceBottomMargin = qMin(image.rect().bottom() - sourceRect.bottom(), SMOOTH_MARGIN); + sourceRightMargin = qMin(imageRect.right() - sourceRect.right(), SMOOTH_MARGIN); + sourceBottomMargin = qMin(imageRect.bottom() - sourceRect.bottom(), SMOOTH_MARGIN); sourceRect.adjust( -sourceLeftMargin, -sourceTopMargin, @@ -190,22 +199,32 @@ ); QRect destRect = PaintUtils::containingRect(destRectF); + QRect dSourceRect = QRectF(sourceRect.x() * dpr, sourceRect.y() * dpr, sourceRect.width() * dpr, sourceRect.height() * dpr).toAlignedRect(); + QRectF dDestRectF = QRectF( + dSourceRect.left() * zoom, + dSourceRect.top() * zoom, + dSourceRect.width() * zoom, + dSourceRect.height() * zoom + ); + QRect dDestRect = PaintUtils::containingRect(dDestRectF); + QImage tmp; - tmp = image.copy(sourceRect); + tmp = image.copy(dSourceRect); tmp = tmp.scaled( - destRect.width(), - destRect.height(), + dDestRect.width(), + dDestRect.height(), Qt::IgnoreAspectRatio, // Do not use KeepAspectRatio, it can lead to skipped rows or columns d->mTransformationMode); if (needsSmoothMargins) { tmp = tmp.copy( - destLeftMargin, destTopMargin, - destRect.width() - (destLeftMargin + destRightMargin), - destRect.height() - (destTopMargin + destBottomMargin) + destLeftMargin*dpr, destTopMargin*dpr, + dDestRect.width() - (destLeftMargin + destRightMargin)*dpr, + dDestRect.height() - (destTopMargin + destBottomMargin)*dpr ); } + tmp.setDevicePixelRatio(dpr); emit scaledRect(destRect.left() + destLeftMargin, destRect.top() + destTopMargin, tmp); }