Marble

GenericScanlineTextureMapper.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
9// local
10#include "GenericScanlineTextureMapper.h"
11
12// Qt
13#include <qmath.h>
14#include <QRunnable>
15
16// Marble
17#include "GeoPainter.h"
18#include "MarbleDirs.h"
19#include "MarbleDebug.h"
20#include "ScanlineTextureMapperContext.h"
21#include "StackedTileLoader.h"
22#include "TextureColorizer.h"
23#include "ViewParams.h"
24#include "ViewportParams.h"
25#include "MathHelper.h"
26#include "AbstractProjection.h"
27
28using namespace Marble;
29
30class GenericScanlineTextureMapper::RenderJob : public QRunnable
31{
32public:
33 RenderJob( StackedTileLoader *tileLoader, int tileLevel, QImage *canvasImage, const ViewportParams *viewport, MapQuality mapQuality, int yTop, int yBottom );
34
35 void run() override;
36
37private:
38 StackedTileLoader *const m_tileLoader;
39 const int m_tileLevel;
40 QImage *const m_canvasImage;
41 const ViewportParams *const m_viewport;
42 const MapQuality m_mapQuality;
43 const int m_yTop;
44 const int m_yBottom;
45};
46
47GenericScanlineTextureMapper::RenderJob::RenderJob( StackedTileLoader *tileLoader, int tileLevel, QImage *canvasImage, const ViewportParams *viewport, MapQuality mapQuality, int yTop, int yBottom )
48 : m_tileLoader( tileLoader ),
49 m_tileLevel( tileLevel ),
50 m_canvasImage( canvasImage ),
51 m_viewport( viewport ),
52 m_mapQuality( mapQuality ),
53 m_yTop( yTop ),
54 m_yBottom( yBottom )
55{
56}
57
58
59GenericScanlineTextureMapper::GenericScanlineTextureMapper( StackedTileLoader *tileLoader )
60 : TextureMapperInterface()
61 , m_tileLoader( tileLoader )
62 , m_radius( 0 )
63 , m_threadPool()
64{
65}
66
67void GenericScanlineTextureMapper::mapTexture( GeoPainter *painter,
68 const ViewportParams *viewport,
69 int tileZoomLevel,
70 const QRect &dirtyRect,
71 TextureColorizer *texColorizer )
72{
73 if ( m_canvasImage.size() != viewport->size() || m_radius != viewport->radius() ) {
74 const QImage::Format optimalFormat = ScanlineTextureMapperContext::optimalCanvasImageFormat( viewport );
75
76 if ( m_canvasImage.size() != viewport->size() || m_canvasImage.format() != optimalFormat ) {
77 m_canvasImage = QImage( viewport->size(), optimalFormat );
78 }
79
80 if ( !viewport->mapCoversViewport() ) {
81 m_canvasImage.fill( 0 );
82 }
83
84 m_radius = viewport->radius();
85 m_repaintNeeded = true;
86 }
87
88 if ( m_repaintNeeded ) {
89 mapTexture( viewport, tileZoomLevel, painter->mapQuality() );
90
91 if ( texColorizer ) {
92 texColorizer->colorize( &m_canvasImage, viewport, painter->mapQuality() );
93 }
94
95 m_repaintNeeded = false;
96 }
97
98 const int radius = viewport->radius() * viewport->currentProjection()->clippingRadius();
99
100 QRect rect( viewport->width() / 2 - radius, viewport->height() / 2 - radius,
101 2 * radius, 2 * radius);
102 rect = rect.intersected( dirtyRect );
103 painter->drawImage( rect, m_canvasImage, rect );
104}
105
106void GenericScanlineTextureMapper::mapTexture( const ViewportParams *viewport, int tileZoomLevel, MapQuality mapQuality )
107{
108 // Reset backend
109 m_tileLoader->resetTilehash();
110
111 const int imageHeight = viewport->height();
112 const qint64 radius = viewport->radius() * viewport->currentProjection()->clippingRadius();
113
114 // Calculate the actual y-range of the map on the screen
115 const int skip = ( mapQuality == LowQuality ) ? 1
116 : 0;
117 const int yTop = ( imageHeight / 2 - radius >= 0 ) ? imageHeight / 2 - radius
118 : 0;
119 const int yBottom = ( yTop == 0 ) ? imageHeight - skip
120 : yTop + radius + radius - skip;
121
122 const int numThreads = m_threadPool.maxThreadCount();
123 const int yStep = qCeil(qreal( yBottom - yTop ) / qreal(numThreads));
124 for ( int i = 0; i < numThreads; ++i ) {
125 const int yStart = yTop + i * yStep;
126 const int yEnd = qMin(yBottom, yTop + (i + 1) * yStep);
127 QRunnable *const job = new RenderJob( m_tileLoader, tileZoomLevel, &m_canvasImage, viewport, mapQuality, yStart, yEnd );
128 m_threadPool.start( job );
129 }
130
131 m_threadPool.waitForDone();
132
133 m_tileLoader->cleanupTilehash();
134}
135
136void GenericScanlineTextureMapper::RenderJob::run()
137{
138 const int imageWidth = m_canvasImage->width();
139 const int imageHeight = m_canvasImage->height();
140 const qint64 radius = m_viewport->radius();
141
142 const bool interlaced = ( m_mapQuality == LowQuality );
143 const bool highQuality = ( m_mapQuality == HighQuality
144 || m_mapQuality == PrintQuality );
145 const bool printQuality = ( m_mapQuality == PrintQuality );
146
147 // Evaluate the degree of interpolation
148 const int n = ScanlineTextureMapperContext::interpolationStep( m_viewport, m_mapQuality );
149
150 // Calculate north pole position to decrease pole distortion later on
151 qreal northPoleX, northPoleY;
152 bool globeHidesNorthPole;
153 GeoDataCoordinates northPole(0, m_viewport->currentProjection()->maxLat(), 0);
154 m_viewport->screenCoordinates(northPole, northPoleX, northPoleY, globeHidesNorthPole );
155
156 // initialize needed variables that are modified during texture mapping:
157
158 ScanlineTextureMapperContext context( m_tileLoader, m_tileLevel );
159
160 qreal clipRadius = radius * m_viewport->currentProjection()->clippingRadius();
161
162
163 // Paint the map.
164 for ( int y = m_yTop; y < m_yBottom; ++y ) {
165
166 // rx is the radius component in x direction
167 const int rx = (int)sqrt( (qreal)( clipRadius * clipRadius
168 - ( ( y - imageHeight / 2 )
169 * ( y - imageHeight / 2 ) ) ) );
170
171 // Calculate the actual x-range of the map within the current scanline.
172 //
173 // If the circular border of the earth disk is still visible then xLeft
174 // equals the scanline position of the most left pixel that gets covered
175 // by the earth disk. In terms of math this equals the half image width minus
176 // the radius component on the current scanline in x direction ("rx").
177 //
178 // If the zoom factor is high enough then the whole screen gets covered
179 // by the earth and the border of the earth disk isn't visible anymore.
180 // In that situation xLeft equals zero.
181 // For xRight the situation is similar.
182
183 const int xLeft = ( imageWidth / 2 - rx > 0 ) ? imageWidth / 2 - rx
184 : 0;
185 const int xRight = ( imageWidth / 2 - rx > 0 ) ? xLeft + rx + rx
186 : imageWidth;
187
188 QRgb * scanLine = (QRgb*)( m_canvasImage->scanLine( y ) ) + xLeft;
189
190 const int xIpLeft = ( imageWidth / 2 - rx > 0 ) ? n * (int)( xLeft / n + 1 )
191 : 1;
192 const int xIpRight = ( imageWidth / 2 - rx > 0 ) ? n * (int)( xRight / n - 1 )
193 : n * (int)( xRight / n - 1 ) + 1;
194
195 // Decrease pole distortion due to linear approximation ( y-axis )
196 bool crossingPoleArea = false;
197 if ( !globeHidesNorthPole
198 && northPoleY - ( n * 0.75 ) <= y
199 && northPoleY + ( n * 0.75 ) >= y )
200 {
201 crossingPoleArea = true;
202 }
203
204 int ncount = 0;
205
206
207 for ( int x = xLeft; x < xRight; ++x ) {
208
209 // Prepare for interpolation
210 const int leftInterval = xIpLeft + ncount * n;
211
212 bool interpolate = false;
213
214 if ( x >= xIpLeft && x <= xIpRight ) {
215
216 // Decrease pole distortion due to linear approximation ( x-axis )
217 if ( crossingPoleArea
218 && northPoleX >= leftInterval + n
219 && northPoleX < leftInterval + 2 * n
220 && x < leftInterval + 3 * n )
221 {
222 interpolate = false;
223 }
224 else {
225 x += n - 1;
226 interpolate = !printQuality;
227 ++ncount;
228 }
229 }
230 else
231 interpolate = false;
232
233 qreal lon;
234 qreal lat;
235 m_viewport->geoCoordinates(x,y, lon, lat, GeoDataCoordinates::Radian);
236
237 if ( interpolate ) {
238 if ( highQuality )
239 context.pixelValueApproxF( lon, lat, scanLine, n );
240 else
241 context.pixelValueApprox( lon, lat, scanLine, n );
242
243 scanLine += ( n - 1 );
244 }
245
246 if ( x < imageWidth ) {
247 if ( highQuality )
248 context.pixelValueF( lon, lat, scanLine );
249 else
250 context.pixelValue( lon, lat, scanLine );
251 }
252
253 ++scanLine;
254 }
255
256 // copy scanline to improve performance
257 if ( interlaced && y + 1 < m_yBottom ) {
258
259 const int pixelByteSize = m_canvasImage->bytesPerLine() / imageWidth;
260
261 memcpy( m_canvasImage->scanLine( y + 1 ) + xLeft * pixelByteSize,
262 m_canvasImage->scanLine( y ) + xLeft * pixelByteSize,
263 ( xRight - xLeft ) * pixelByteSize );
264 ++y;
265 }
266 }
267}
This file contains the headers for AbstractProjection.
This file contains the headers for ViewParameters.
This file contains the headers for ViewportParams.
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:89
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.
bool geoCoordinates(const int x, const int y, qreal &lon, qreal &lat, GeoDataCoordinates::Unit unit=GeoDataCoordinates::Degree) const
Get the earth coordinates corresponding to a pixel 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 Tue Mar 26 2024 11:18:17 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.