Skip to content

Commit

Permalink
Merge pull request #1622 from Karry/hidpi
Browse files Browse the repository at this point in the history
HiDPI support in tile map renderer
  • Loading branch information
Framstag authored Nov 14, 2024
2 parents 6687977 + d721e81 commit 0ca5593
Show file tree
Hide file tree
Showing 10 changed files with 95 additions and 36 deletions.
4 changes: 4 additions & 0 deletions libosmscout-client-qt/include/osmscoutclientqt/MapRenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <QSettings>
#include <QMutex>
#include <QPainter>
#include <QScreen>

namespace osmscout {

Expand Down Expand Up @@ -97,6 +98,7 @@ class OSMSCOUT_CLIENT_QT_API MapRenderer : public QObject {
#endif

double mapDpi;
double screenPixelRatio{1.0};
bool renderSea;

QString fontName;
Expand Down Expand Up @@ -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,
Expand Down
5 changes: 5 additions & 0 deletions libosmscout-client-qt/include/osmscoutclientqt/MapWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -309,6 +310,8 @@ private slots:

void onResize();

void onWindowChanged(QQuickWindow *window);

private:
void setupInputHandler(InputHandler *newGesture);

Expand All @@ -320,6 +323,8 @@ private slots:
*/
osmscout::Magnification magnificationByDimension(const Distance &dimension);

void setupRenderer();

public:
MapWidget(QQuickItem* parent = nullptr);
~MapWidget() override;
Expand Down
4 changes: 2 additions & 2 deletions libosmscout-client-qt/include/osmscoutclientqt/TileCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ QDebug& operator<<(QDebug &out, const TileCacheKey &key);
struct TileCacheVal
{
QElapsedTimer lastAccess;
QPixmap image;
QImage image;
size_t epoch;
};

Expand Down Expand Up @@ -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();

Expand Down
17 changes: 17 additions & 0 deletions libosmscout-client-qt/src/osmscoutclientqt/MapRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
{
Expand Down
47 changes: 39 additions & 8 deletions libosmscout-client-qt/src/osmscoutclientqt/MapWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <QtSvg/QSvgRenderer>
#include <QtGlobal>
#include <QQuickWindow>
#include <QGuiApplication>

namespace osmscout {

Expand All @@ -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();
Expand All @@ -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);
Expand All @@ -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);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -1024,14 +1056,13 @@ void MapWidget::SetRenderingType(QString strType)
renderingType=type;

std::map<int,OverlayObjectRef> 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());
}
}
Expand Down
4 changes: 2 additions & 2 deletions libosmscout-client-qt/src/osmscoutclientqt/OSMScoutQt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ void PlaneMapRenderer::DrawMap()

currentImage=new QImage(QSize(currentWidth,
currentHeight),
QImage::Format_RGB32);
QImage::Format_RGBA8888_Premultiplied);
}

osmscout::MapParameter drawParameter;
Expand Down
7 changes: 3 additions & 4 deletions libosmscout-client-qt/src/osmscoutclientqt/TileCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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;
Expand Down
26 changes: 18 additions & 8 deletions libosmscout-client-qt/src/osmscoutclientqt/TiledMapRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

#include <QGuiApplication>
#include <QScreen>
#include <QtCore>

namespace osmscout {
TiledMapRenderer::TiledMapRenderer(QThread *thread,
Expand Down Expand Up @@ -418,11 +419,20 @@ void TiledMapRenderer::onLoadJobFinished(QMap<QString,QMap<osmscout::TileKey,osm
(double)loadXFrom + (double)width/2.0,
(double)loadYFrom + (double)height/2.0);

double osmTileDimension = (double)OSMTile::osmTileOriginalWidth() * (mapDpi / OSMTile::tileDPI() ); // pixels
// For HiDPI screens (screenPixelRatio > 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);
Expand Down Expand Up @@ -468,7 +478,7 @@ void TiledMapRenderer::onLoadJobFinished(QMap<QString,QMap<osmscout::TileKey,osm
osmscout::MercatorProjection projection;
osmscout::Magnification magnification(loadZ);

projection.Set(tileVisualCenter, /* angle */ 0, magnification, mapDpi,
projection.Set(tileVisualCenter, /* angle */ 0, magnification, finalDpi,
canvas.width(), canvas.height());
projection.SetLinearInterpolationUsage(loadZ.Get() >= 10);

Expand Down Expand Up @@ -519,9 +529,9 @@ void TiledMapRenderer::onLoadJobFinished(QMap<QString,QMap<osmscout::TileKey,osm
for (uint32_t x = loadXFrom; x <= loadXTo; ++x){

QImage tile = canvas.copy(
(double)(x - loadXFrom) * osmTileDimension,
(double)(y - loadYFrom) * osmTileDimension,
osmTileDimension, osmTileDimension
(x - loadXFrom) * tileDimension,
(y - loadYFrom) * tileDimension,
tileDimension, tileDimension
);

offlineTileCache.put(loadZ.Get(), x, y, tile, loadEpoch);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,10 @@ bool TiledRenderingHelper::RenderTiles(QPainter &painter,
}
}

// enable subpixel rendering so that magnification up to 2x remains
// as smooth as possible
static constexpr double pitchFactor = 1.5;
// the magnification level is adjusted taking into account the pixel
// ratio of the request and the fixed dpi of the OSM tile
projection.Set(request.coord,
0,
Magnification(request.magnification.GetMagnification()
* pitchFactor * request.dpi / OSMTile::tileDPI()),
OSMTile::tileDPI() / pitchFactor,
request.magnification,
request.dpi,
width,
height);

Expand Down Expand Up @@ -224,8 +218,7 @@ bool TiledRenderingHelper::lookupAndDrawTile(TileCache& tileCache, QPainter& pai
QRectF imageViewport(imageWidth * lookupTileViewport.x(), imageHeight * lookupTileViewport.y(),
imageWidth * lookupTileViewport.width(), imageHeight * lookupTileViewport.height() );

// TODO: support map rotation
painter.drawPixmap(QRectF(x, y, renderTileWidth+overlap, renderTileHeight+overlap), val.image, imageViewport);
painter.drawImage(QRectF(x, y, renderTileWidth+overlap, renderTileHeight+overlap), val.image, imageViewport);
}
lookupTileFound = true;
if (lookupTileZoom == zoomLevel && val.epoch == tileCache.getEpoch()) {
Expand Down Expand Up @@ -300,7 +293,7 @@ void TiledRenderingHelper::lookupAndDrawBottomTileRecursive(TileCache& tileCache
if (!val.image.isNull()){
double imageWidth = val.image.width();
double imageHeight = val.image.height();
painter.drawPixmap(
painter.drawImage(
QRectF(x + tx * (renderTileWidth/tileCnt), y + ty * (renderTileHeight/tileCnt), renderTileWidth/tileCnt + overlap, renderTileHeight/tileCnt + overlap),
val.image,
QRectF(0.0, 0.0, imageWidth, imageHeight));
Expand Down

0 comments on commit 0ca5593

Please sign in to comment.