10 #include "GenericScanlineTextureMapper.h"
17 #include "GeoPainter.h"
18 #include "MarbleDirs.h"
19 #include "MarbleDebug.h"
20 #include "ScanlineTextureMapperContext.h"
21 #include "StackedTileLoader.h"
22 #include "TextureColorizer.h"
25 #include "MathHelper.h"
30 class GenericScanlineTextureMapper::RenderJob :
public QRunnable
39 const int m_tileLevel;
40 QImage *
const m_canvasImage;
48 : m_tileLoader( tileLoader ),
49 m_tileLevel( tileLevel ),
50 m_canvasImage( canvasImage ),
51 m_viewport( viewport ),
52 m_mapQuality( mapQuality ),
59 GenericScanlineTextureMapper::GenericScanlineTextureMapper(
StackedTileLoader *tileLoader )
60 : TextureMapperInterface()
61 , m_tileLoader( tileLoader )
67 void GenericScanlineTextureMapper::mapTexture(
GeoPainter *painter,
70 const QRect &dirtyRect,
71 TextureColorizer *texColorizer )
73 if ( m_canvasImage.size() != viewport->size() || m_radius != viewport->radius() ) {
74 const QImage::Format optimalFormat = ScanlineTextureMapperContext::optimalCanvasImageFormat( viewport );
76 if ( m_canvasImage.size() != viewport->size() || m_canvasImage.format() != optimalFormat ) {
77 m_canvasImage =
QImage( viewport->size(), optimalFormat );
80 if ( !viewport->mapCoversViewport() ) {
81 m_canvasImage.fill( 0 );
84 m_radius = viewport->radius();
85 m_repaintNeeded =
true;
88 if ( m_repaintNeeded ) {
89 mapTexture( viewport, tileZoomLevel, painter->
mapQuality() );
92 texColorizer->colorize( &m_canvasImage, viewport, painter->
mapQuality() );
95 m_repaintNeeded =
false;
98 const int radius = viewport->radius() * viewport->currentProjection()->clippingRadius();
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 );
106 void GenericScanlineTextureMapper::mapTexture(
const ViewportParams *viewport,
int tileZoomLevel,
MapQuality mapQuality )
109 m_tileLoader->resetTilehash();
111 const int imageHeight = viewport->height();
112 const qint64 radius = viewport->radius() * viewport->currentProjection()->clippingRadius();
115 const int skip = ( mapQuality ==
LowQuality ) ? 1
117 const int yTop = ( imageHeight / 2 - radius >= 0 ) ? imageHeight / 2 - radius
119 const int yBottom = ( yTop == 0 ) ? imageHeight - skip
120 : yTop + radius + radius - skip;
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 );
131 m_threadPool.waitForDone();
133 m_tileLoader->cleanupTilehash();
136 void GenericScanlineTextureMapper::RenderJob::run()
138 const int imageWidth = m_canvasImage->width();
139 const int imageHeight = m_canvasImage->height();
140 const qint64 radius = m_viewport->radius();
142 const bool interlaced = ( m_mapQuality ==
LowQuality );
143 const bool highQuality = ( m_mapQuality ==
HighQuality
145 const bool printQuality = ( m_mapQuality ==
PrintQuality );
148 const int n = ScanlineTextureMapperContext::interpolationStep( m_viewport, m_mapQuality );
151 qreal northPoleX, northPoleY;
152 bool globeHidesNorthPole;
154 m_viewport->screenCoordinates(northPole, northPoleX, northPoleY, globeHidesNorthPole );
158 ScanlineTextureMapperContext context( m_tileLoader, m_tileLevel );
160 qreal clipRadius = radius * m_viewport->currentProjection()->clippingRadius();
164 for (
int y = m_yTop; y < m_yBottom; ++y ) {
167 const int rx = (int)sqrt( (qreal)( clipRadius * clipRadius
168 - ( ( y - imageHeight / 2 )
169 * ( y - imageHeight / 2 ) ) ) );
183 const int xLeft = ( imageWidth / 2 - rx > 0 ) ? imageWidth / 2 - rx
185 const int xRight = ( imageWidth / 2 - rx > 0 ) ? xLeft + rx + rx
188 QRgb * scanLine = (QRgb*)( m_canvasImage->scanLine( y ) ) + xLeft;
190 const int xIpLeft = ( imageWidth / 2 - rx > 0 ) ? n * (
int)( xLeft / n + 1 )
192 const int xIpRight = ( imageWidth / 2 - rx > 0 ) ? n * (
int)( xRight / n - 1 )
193 : n * (
int)( xRight / n - 1 ) + 1;
196 bool crossingPoleArea =
false;
197 if ( !globeHidesNorthPole
198 && northPoleY - ( n * 0.75 ) <= y
199 && northPoleY + ( n * 0.75 ) >= y )
201 crossingPoleArea =
true;
207 for (
int x = xLeft; x < xRight; ++x ) {
210 const int leftInterval = xIpLeft + ncount * n;
212 bool interpolate =
false;
214 if ( x >= xIpLeft && x <= xIpRight ) {
217 if ( crossingPoleArea
218 && northPoleX >= leftInterval + n
219 && northPoleX < leftInterval + 2 * n
220 && x < leftInterval + 3 * n )
226 interpolate = !printQuality;
235 m_viewport->geoCoordinates(x,y, lon, lat, GeoDataCoordinates::Radian);
239 context.pixelValueApproxF( lon, lat, scanLine, n );
241 context.pixelValueApprox( lon, lat, scanLine, n );
243 scanLine += ( n - 1 );
246 if ( x < imageWidth ) {
248 context.pixelValueF( lon, lat, scanLine );
250 context.pixelValue( lon, lat, scanLine );
257 if ( interlaced && y + 1 < m_yBottom ) {
259 const int pixelByteSize = m_canvasImage->bytesPerLine() / imageWidth;
261 memcpy( m_canvasImage->scanLine( y + 1 ) + xLeft * pixelByteSize,
262 m_canvasImage->scanLine( y ) + xLeft * pixelByteSize,
263 ( xRight - xLeft ) * pixelByteSize );