Marble

MercatorScanlineTextureMapper.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2007 Carlos Licea <carlos _licea@hotmail.com>
4// SPDX-FileCopyrightText: 2008 Inge Wallin <inge@lysator.liu.se>
5// SPDX-FileCopyrightText: 2011 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
6//
7
8// local
9#include "MercatorScanlineTextureMapper.h"
10
11// posix
12#include <cmath>
13
14// Qt
15#include <QRunnable>
16
17// Marble
18#include "AbstractProjection.h"
19#include "GeoPainter.h"
20#include "MarbleDebug.h"
21#include "MathHelper.h"
22#include "ScanlineTextureMapperContext.h"
23#include "StackedTileLoader.h"
24#include "TextureColorizer.h"
25#include "ViewportParams.h"
26
27using namespace Marble;
28
29class MercatorScanlineTextureMapper::RenderJob : public QRunnable
30{
31public:
32 RenderJob(StackedTileLoader *tileLoader, int tileLevel, QImage *canvasImage, const ViewportParams *viewport, MapQuality mapQuality, int yTop, int yBottom);
33
34 void run() override;
35
36private:
37 StackedTileLoader *const m_tileLoader;
38 const int m_tileLevel;
39 QImage *const m_canvasImage;
40 const ViewportParams *const m_viewport;
41 const MapQuality m_mapQuality;
42 const int m_yPaintedTop;
43 const int m_yPaintedBottom;
44};
45
46MercatorScanlineTextureMapper::RenderJob::RenderJob(StackedTileLoader *tileLoader,
47 int tileLevel,
48 QImage *canvasImage,
49 const ViewportParams *viewport,
50 MapQuality mapQuality,
51 int yTop,
52 int yBottom)
53 : m_tileLoader(tileLoader)
54 , m_tileLevel(tileLevel)
55 , m_canvasImage(canvasImage)
56 , m_viewport(viewport)
57 , m_mapQuality(mapQuality)
58 , m_yPaintedTop(yTop)
59 , m_yPaintedBottom(yBottom)
60{
61}
62
63MercatorScanlineTextureMapper::MercatorScanlineTextureMapper(StackedTileLoader *tileLoader)
64 : TextureMapperInterface()
65 , m_tileLoader(tileLoader)
66 , m_radius(0)
67 , m_oldYPaintedTop(0)
68{
69}
70
71void MercatorScanlineTextureMapper::mapTexture(GeoPainter *painter,
72 const ViewportParams *viewport,
73 int tileZoomLevel,
74 const QRect &dirtyRect,
75 TextureColorizer *texColorizer)
76{
77 if (m_canvasImage.size() != viewport->size() || m_radius != viewport->radius()) {
78 const QImage::Format optimalFormat = ScanlineTextureMapperContext::optimalCanvasImageFormat(viewport);
79
80 if (m_canvasImage.size() != viewport->size() || m_canvasImage.format() != optimalFormat) {
81 m_canvasImage = QImage(viewport->size(), optimalFormat);
82 }
83
84 if (!viewport->mapCoversViewport()) {
85 m_canvasImage.fill(0);
86 }
87
88 m_radius = viewport->radius();
89 m_repaintNeeded = true;
90 }
91
92 if (m_repaintNeeded) {
93 mapTexture(viewport, tileZoomLevel, painter->mapQuality());
94
95 if (texColorizer) {
96 texColorizer->colorize(&m_canvasImage, viewport, painter->mapQuality());
97 }
98
99 m_repaintNeeded = false;
100 }
101
102 painter->drawImage(dirtyRect, m_canvasImage, dirtyRect);
103}
104
105void MercatorScanlineTextureMapper::mapTexture(const ViewportParams *viewport, int tileZoomLevel, MapQuality mapQuality)
106{
107 // Reset backend
108 m_tileLoader->resetTilehash();
109
110 // Initialize needed constants:
111
112 const int imageHeight = m_canvasImage.height();
113
114 // Calculate y-range the represented by the center point, yTop and
115 // what actually can be painted
116
117 qreal realYTop, realYBottom, dummyX;
118 GeoDataCoordinates yNorth(0, viewport->currentProjection()->maxLat(), 0);
119 GeoDataCoordinates ySouth(0, viewport->currentProjection()->minLat(), 0);
120 viewport->screenCoordinates(yNorth, dummyX, realYTop);
121 viewport->screenCoordinates(ySouth, dummyX, realYBottom);
122
123 const int yTop = qBound(qreal(0.0), realYTop, qreal(imageHeight));
124 int yPaintedTop = yTop;
125 int yPaintedBottom = qBound(qreal(0.0), realYBottom, qreal(imageHeight));
126
127 yPaintedTop = qBound(0, yPaintedTop, imageHeight);
128 yPaintedBottom = qBound(0, yPaintedBottom, imageHeight);
129
130 const int numThreads = m_threadPool.maxThreadCount();
131 const int yStep = (yPaintedBottom - yPaintedTop) / numThreads;
132 for (int i = 0; i < numThreads; ++i) {
133 const int yStart = yPaintedTop + i * yStep;
134 const int yEnd = (i == numThreads - 1) ? yPaintedBottom : yPaintedTop + (i + 1) * yStep;
135 QRunnable *const job = new RenderJob(m_tileLoader, tileZoomLevel, &m_canvasImage, viewport, mapQuality, yStart, yEnd);
136 m_threadPool.start(job);
137 }
138
139 // Remove unused lines
140 const int clearStart = (yPaintedTop - m_oldYPaintedTop <= 0) ? yPaintedBottom : 0;
141 const int clearStop = (yPaintedTop - m_oldYPaintedTop <= 0) ? imageHeight : yTop;
142
143 QRgb *const itClearBegin = (QRgb *)(m_canvasImage.scanLine(clearStart));
144 QRgb *const itClearEnd = (QRgb *)(m_canvasImage.scanLine(clearStop));
145
146 for (QRgb *it = itClearBegin; it < itClearEnd; ++it) {
147 *(it) = 0;
148 }
149
150 m_threadPool.waitForDone();
151
152 m_oldYPaintedTop = yPaintedTop;
153
154 m_tileLoader->cleanupTilehash();
155}
156
157void MercatorScanlineTextureMapper::RenderJob::run()
158{
159 // Scanline based algorithm to do texture mapping
160
161 const int imageHeight = m_canvasImage->height();
162 const int imageWidth = m_canvasImage->width();
163 const qint64 radius = m_viewport->radius();
164 // Calculate how many degrees are being represented per pixel.
165 const float rad2Pixel = (float)(2 * radius) / M_PI;
166 const qreal pixel2Rad = 1.0 / rad2Pixel;
167
168 const bool interlaced = (m_mapQuality == LowQuality);
169 const bool highQuality = (m_mapQuality == HighQuality || m_mapQuality == PrintQuality);
170 const bool printQuality = (m_mapQuality == PrintQuality);
171
172 // Evaluate the degree of interpolation
173 const int n = ScanlineTextureMapperContext::interpolationStep(m_viewport, m_mapQuality);
174
175 // Calculate translation of center point
176 const qreal centerLon = m_viewport->centerLongitude();
177 const qreal centerLat = m_viewport->centerLatitude();
178
179 const int yCenterOffset = (int)(asinh(tan(centerLat)) * rad2Pixel);
180
181 qreal leftLon = +centerLon - (imageWidth / 2 * pixel2Rad);
182 while (leftLon < -M_PI)
183 leftLon += 2 * M_PI;
184 while (leftLon > M_PI)
185 leftLon -= 2 * M_PI;
186
187 const int maxInterpolationPointX = n * (int)(imageWidth / n - 1) + 1;
188
189 // initialize needed variables that are modified during texture mapping:
190
191 ScanlineTextureMapperContext context(m_tileLoader, m_tileLevel);
192
193 // Scanline based algorithm to do texture mapping
194
195 for (int y = m_yPaintedTop; y < m_yPaintedBottom; ++y) {
196 QRgb *scanLine = (QRgb *)(m_canvasImage->scanLine(y));
197
198 qreal lon = leftLon;
199 const qreal lat = gd(((imageHeight / 2 + yCenterOffset) - y) * pixel2Rad);
200
201 for (int x = 0; x < imageWidth; ++x) {
202 // Prepare for interpolation
203 bool interpolate = false;
204 if (x > 0 && x <= maxInterpolationPointX) {
205 x += n - 1;
206 lon += (n - 1) * pixel2Rad;
207 interpolate = !printQuality;
208 } else {
209 interpolate = false;
210 }
211
212 if (lon < -M_PI)
213 lon += 2 * M_PI;
214 if (lon > M_PI)
215 lon -= 2 * M_PI;
216
217 if (interpolate) {
218 if (highQuality)
219 context.pixelValueApproxF(lon, lat, scanLine, n);
220 else
221 context.pixelValueApprox(lon, lat, scanLine, n);
222
223 scanLine += (n - 1);
224 }
225
226 if (x < imageWidth) {
227 if (highQuality)
228 context.pixelValueF(lon, lat, scanLine);
229 else
230 context.pixelValue(lon, lat, scanLine);
231 }
232
233 ++scanLine;
234 lon += pixel2Rad;
235 }
236
237 // copy scanline to improve performance
238 if (interlaced && y + 1 < m_yPaintedBottom) {
239 const int pixelByteSize = m_canvasImage->bytesPerLine() / imageWidth;
240
241 memcpy(m_canvasImage->scanLine(y + 1), m_canvasImage->scanLine(y), imageWidth * pixelByteSize);
242 ++y;
243 }
244 }
245}
This file contains the headers for AbstractProjection.
This file contains the headers for ViewportParams.
qreal minLat() const
Returns the arbitrarily chosen minimum (southern) latitude.
qreal maxLat() const
Returns the arbitrarily chosen maximum (northern) latitude.
A 3d point representation.
A painter that allows to draw geometric primitives on the map.
Definition GeoPainter.h:86
MapQuality mapQuality() const
Returns the map quality.
void drawImage(const GeoDataCoordinates &centerPosition, const QImage &image)
Draws an image at the given position. The image is placed with its center located at the given center...
Tile loading from a quad tree.
void cleanupTilehash()
Cleans up the internal tile hash.
void resetTilehash()
Resets the internal tile hash.
A public class that controls what is visible in the viewport of a Marble map.
bool screenCoordinates(const qreal lon, const qreal lat, qreal &x, qreal &y) const
Get the screen coordinates corresponding to geographical coordinates in the map.
Binds a QML item to a specific geodetic location in screen coordinates.
MapQuality
This enum is used to choose the map quality shown in the view.
@ HighQuality
High quality (e.g. antialiasing for lines)
@ PrintQuality
Print quality.
@ LowQuality
Low resolution (e.g. interlaced)
qsizetype bytesPerLine() const const
void fill(Qt::GlobalColor color)
Format format() const const
int height() const const
uchar * scanLine(int i)
QSize size() const const
int width() const const
void start(Callable &&callableToRun, int priority)
bool waitForDone(int msecs)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 8 2024 12:02:44 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.