Marble

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

KDE's Doxygen guidelines are available online.