diff --git a/libosmscout-client-qt/include/osmscoutclientqt/MapRenderer.h b/libosmscout-client-qt/include/osmscoutclientqt/MapRenderer.h index b8e87d4fa..139c09505 100644 --- a/libosmscout-client-qt/include/osmscoutclientqt/MapRenderer.h +++ b/libosmscout-client-qt/include/osmscoutclientqt/MapRenderer.h @@ -33,6 +33,7 @@ #include #include #include +#include namespace osmscout { @@ -97,6 +98,7 @@ class OSMSCOUT_CLIENT_QT_API MapRenderer : public QObject { #endif double mapDpi; + double screenPixelRatio{1.0}; bool renderSea; QString fontName; @@ -176,6 +178,8 @@ public slots: virtual void onShowAltLanguageChanged(bool); virtual void onUnitsChanged(const QString&); + virtual void SetScreen(const QScreen*); + protected: MapRenderer(QThread *thread, SettingsRef settings, diff --git a/libosmscout-client-qt/include/osmscoutclientqt/MapWidget.h b/libosmscout-client-qt/include/osmscoutclientqt/MapWidget.h index c45fe7f1f..5c9691164 100644 --- a/libosmscout-client-qt/include/osmscoutclientqt/MapWidget.h +++ b/libosmscout-client-qt/include/osmscoutclientqt/MapWidget.h @@ -188,6 +188,7 @@ class OSMSCOUT_CLIENT_QT_API MapWidget : public QQuickPaintedItem void styleErrorsChanged(); void databaseLoaded(osmscout::GeoBox); void renderingTypeChanged(QString type); + void screenChanged(QScreen*); public slots: void changeView(const MapView &view); @@ -309,6 +310,8 @@ private slots: void onResize(); + void onWindowChanged(QQuickWindow *window); + private: void setupInputHandler(InputHandler *newGesture); @@ -320,6 +323,8 @@ private slots: */ osmscout::Magnification magnificationByDimension(const Distance &dimension); + void setupRenderer(); + public: MapWidget(QQuickItem* parent = nullptr); ~MapWidget() override; diff --git a/libosmscout-client-qt/include/osmscoutclientqt/TileCache.h b/libosmscout-client-qt/include/osmscoutclientqt/TileCache.h index d33e6d1c2..5589fb793 100644 --- a/libosmscout-client-qt/include/osmscoutclientqt/TileCache.h +++ b/libosmscout-client-qt/include/osmscoutclientqt/TileCache.h @@ -70,7 +70,7 @@ QDebug& operator<<(QDebug &out, const TileCacheKey &key); struct TileCacheVal { QElapsedTimer lastAccess; - QPixmap image; + QImage image; size_t epoch; }; @@ -141,7 +141,7 @@ class OSMSCOUT_CLIENT_QT_API TileCache : public QObject * @return true if there was such request */ bool removeRequest(uint32_t zoomLevel, uint32_t x, uint32_t y); - void put(uint32_t zoomLevel, uint32_t x, uint32_t y, QImage image, size_t epoch = 0); + void put(uint32_t zoomLevel, uint32_t x, uint32_t y, const QImage &image, size_t epoch = 0); void cleanupCache(); diff --git a/libosmscout-client-qt/src/osmscoutclientqt/MapRenderer.cpp b/libosmscout-client-qt/src/osmscoutclientqt/MapRenderer.cpp index 7c8c12136..feec5b3af 100644 --- a/libosmscout-client-qt/src/osmscoutclientqt/MapRenderer.cpp +++ b/libosmscout-client-qt/src/osmscoutclientqt/MapRenderer.cpp @@ -157,6 +157,23 @@ void MapRenderer::onUnitsChanged(const QString& units) emit Redraw(); } +void MapRenderer::SetScreen(const QScreen *screen) +{ + bool changed=false; + { + QMutexLocker locker(&lock); + if (this->screenPixelRatio != screen->devicePixelRatio()) { + this->screenPixelRatio = screen->devicePixelRatio(); + log.Debug() << "Screen pixel ratio: " << this->screenPixelRatio; + changed = true; + } + } + if (changed) { + InvalidateVisualCache(); + emit Redraw(); + } +} + void MapRenderer::addOverlayObject(int id, const OverlayObjectRef& obj) { { diff --git a/libosmscout-client-qt/src/osmscoutclientqt/MapWidget.cpp b/libosmscout-client-qt/src/osmscoutclientqt/MapWidget.cpp index 7139ad7f3..2c9b1fadf 100644 --- a/libosmscout-client-qt/src/osmscoutclientqt/MapWidget.cpp +++ b/libosmscout-client-qt/src/osmscoutclientqt/MapWidget.cpp @@ -27,6 +27,7 @@ #include #include #include +#include namespace osmscout { @@ -45,7 +46,8 @@ MapWidget::MapWidget(QQuickItem* parent) setAcceptTouchEvents(true); #endif - renderer = OSMScoutQt::GetInstance().MakeMapRenderer(renderingType); + setupRenderer(); + auto settings=OSMScoutQt::GetInstance().GetSettings(); DBThreadRef dbThread = OSMScoutQt::GetInstance().GetDBThread(); @@ -57,9 +59,6 @@ MapWidget::MapWidget(QQuickItem* parent) tapRecognizer.setPhysicalDpi(dbThread->GetPhysicalDpi()); - connect(renderer, &MapRenderer::Redraw, - this, &MapWidget::redraw); - connect(&tapRecognizer, &TapRecognizer::tap, this, &MapWidget::onTap); connect(&tapRecognizer, &TapRecognizer::doubleTap, this, &MapWidget::onDoubleTap); connect(&tapRecognizer, &TapRecognizer::longTap, this, &MapWidget::onLongTap); @@ -68,6 +67,7 @@ MapWidget::MapWidget(QQuickItem* parent) connect(this, &QQuickItem::widthChanged, this, &MapWidget::onResize); connect(this, &QQuickItem::heightChanged, this, &MapWidget::onResize); + connect(this, &QQuickItem::windowChanged, this, &MapWidget::onWindowChanged); connect(&iconAnimation, &IconAnimation::update, this, &MapWidget::redraw); @@ -100,6 +100,38 @@ MapWidget::~MapWidget() } } +void MapWidget::setupRenderer() +{ + if (renderer) { + renderer->deleteLater(); + } + + renderer = OSMScoutQt::GetInstance().MakeMapRenderer(renderingType); + + connect(renderer, &MapRenderer::Redraw, + this, &MapWidget::redraw); + + connect(this, &MapWidget::screenChanged, + renderer, &MapRenderer::SetScreen, + Qt::QueuedConnection); + + QQuickWindow *window = this->window(); + if (window) { + emit screenChanged(window->screen()); + } else { + emit screenChanged(QGuiApplication::primaryScreen()); + } +} + +void MapWidget::onWindowChanged(QQuickWindow *window) +{ + if (window) { + emit screenChanged(window->screen()); + connect(window, &QWindow::screenChanged, + this, &MapWidget::screenChanged); + } +} + void MapWidget::translateToTouch(QMouseEvent* event, Qt::TouchPointStates states) { assert(event); @@ -1024,14 +1056,13 @@ void MapWidget::SetRenderingType(QString strType) renderingType=type; std::map overlayWays = renderer->getOverlayObjects(); - renderer->deleteLater(); - renderer = OSMScoutQt::GetInstance().MakeMapRenderer(renderingType); + setupRenderer(); + for (auto &p:overlayWays){ renderer->addOverlayObject(p.first, p.second); } - connect(renderer, &MapRenderer::Redraw, - this, &MapWidget::redraw); + emit renderingTypeChanged(GetRenderingType()); } } diff --git a/libosmscout-client-qt/src/osmscoutclientqt/OSMScoutQt.cpp b/libosmscout-client-qt/src/osmscoutclientqt/OSMScoutQt.cpp index e0d710945..70dccf4c3 100644 --- a/libosmscout-client-qt/src/osmscoutclientqt/OSMScoutQt.cpp +++ b/libosmscout-client-qt/src/osmscoutclientqt/OSMScoutQt.cpp @@ -97,8 +97,8 @@ bool OSMScoutQtBuilder::Init() * 130 - PC (24" FullHD) * 100 - Qt default (reported by SailfishOS < 2.0.1) */ - QScreen *srn=QGuiApplication::screens().at(0); - double physicalDpi = (double)srn->physicalDotsPerInch(); + QScreen *srn=QGuiApplication::primaryScreen(); + double physicalDpi = srn ? (double)srn->physicalDotsPerInch() : 100; QLocale locale; QString defaultUnits; diff --git a/libosmscout-client-qt/src/osmscoutclientqt/PlaneMapRenderer.cpp b/libosmscout-client-qt/src/osmscoutclientqt/PlaneMapRenderer.cpp index 705d28426..8a82f8cad 100644 --- a/libosmscout-client-qt/src/osmscoutclientqt/PlaneMapRenderer.cpp +++ b/libosmscout-client-qt/src/osmscoutclientqt/PlaneMapRenderer.cpp @@ -326,7 +326,7 @@ void PlaneMapRenderer::DrawMap() currentImage=new QImage(QSize(currentWidth, currentHeight), - QImage::Format_RGB32); + QImage::Format_RGBA8888_Premultiplied); } osmscout::MapParameter drawParameter; diff --git a/libosmscout-client-qt/src/osmscoutclientqt/TileCache.cpp b/libosmscout-client-qt/src/osmscoutclientqt/TileCache.cpp index f3bce8ecf..1fcb69d3b 100644 --- a/libosmscout-client-qt/src/osmscoutclientqt/TileCache.cpp +++ b/libosmscout-client-qt/src/osmscoutclientqt/TileCache.cpp @@ -174,7 +174,7 @@ TileCacheVal TileCache::get(uint32_t zoomLevel, uint32_t x, uint32_t y) TileCacheKey key = {zoomLevel, x, y}; if (!tiles.contains(key)){ qWarning() << this << "No tile in cache for key " << key; - return {QElapsedTimer(), QPixmap(), epoch}; // throw std::underflow_error ? + return {QElapsedTimer(), QImage(), epoch}; // throw std::underflow_error ? } TileCacheVal val = tiles.value(key); val.lastAccess.start(); @@ -193,14 +193,13 @@ bool TileCache::removeRequest(uint32_t zoomLevel, uint32_t x, uint32_t y) return requests.remove(key) > 0; } -void TileCache::put(uint32_t zoomLevel, uint32_t x, uint32_t y, QImage image, size_t epoch) +void TileCache::put(uint32_t zoomLevel, uint32_t x, uint32_t y, const QImage &image, size_t epoch) { - QPixmap pixmap = QPixmap::fromImage(image); removeRequest(zoomLevel, x, y); TileCacheKey key = {zoomLevel, x, y}; QElapsedTimer now; now.start(); - TileCacheVal val = {now, pixmap, epoch}; + TileCacheVal val = {now, image, epoch}; #ifdef DEBUG_TILE_CACHE qDebug() << this << "inserting tile" << key; diff --git a/libosmscout-client-qt/src/osmscoutclientqt/TiledMapRenderer.cpp b/libosmscout-client-qt/src/osmscoutclientqt/TiledMapRenderer.cpp index 4b66d6b08..66da5ee61 100644 --- a/libosmscout-client-qt/src/osmscoutclientqt/TiledMapRenderer.cpp +++ b/libosmscout-client-qt/src/osmscoutclientqt/TiledMapRenderer.cpp @@ -28,6 +28,7 @@ #include #include +#include namespace osmscout { TiledMapRenderer::TiledMapRenderer(QThread *thread, @@ -418,11 +419,20 @@ void TiledMapRenderer::onLoadJobFinished(QMap 1) tiles as up-scaled before displaying. When there is ratio 2.0, 100px on Qt canvas + // is displayed as 200px on the screen. To provide best results on HiDPI screen, we upscale tiles by this pixel ratio. + double finalDpi = mapDpi * this->screenPixelRatio; - QImage canvas((double)width * osmTileDimension, - (double)height * osmTileDimension, - QImage::Format_ARGB32_Premultiplied); // TODO: verify best format with profiler (callgrind) + uint32_t tileDimension = double(OSMTile::osmTileOriginalWidth()) * (finalDpi / OSMTile::tileDPI()); // pixels + + // older/mobile OpenGL (without GL_ARB_texture_non_power_of_two) requires textures with size of power of two + // we should provide tiles with required size to avoid scaling in QOpenGLTextureCache::bindTexture + tileDimension = qNextPowerOfTwo(tileDimension - 1); + finalDpi = (double(tileDimension) / double(OSMTile::osmTileOriginalWidth())) * OSMTile::tileDPI(); + + QImage canvas(width * tileDimension, + height * tileDimension, + QImage::Format_RGBA8888_Premultiplied); QColor transparent = QColor::fromRgbF(1, 1, 1, 0.0); canvas.fill(transparent); @@ -468,7 +478,7 @@ void TiledMapRenderer::onLoadJobFinished(QMap= 10); @@ -519,9 +529,9 @@ void TiledMapRenderer::onLoadJobFinished(QMap